Django Channels, показывающие текущих пользователей в комнате
Я пытаюсь показать текущих пользователей, которые подключены к тому же вебсокету (в моем случае чат), но у меня есть небольшая проблема. Я хочу создать переменную, которая будет хранить пользователей и отправлять их через вебсокет на фронтенд, я достиг этого, но вот в чем проблема.
Consumers.py - подход 1
class ChatRoomConsumer(AsyncWebsocketConsumer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.users: list = []
@database_sync_to_async
def create_msg(self, user_id=None, message=None, room=None):
if user_id is not None:
sender = User.objects.get(id=user_id)
msg = Message.objects.create(
author=sender, message=message, room_name=room)
msg.save()
return msg
else:
get_msgs = Message.objects.filter(room_name__in=[room])
serializer = MessageSerializer(get_msgs, many=True)
return serializer.data
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = f'chat_{self.room_name}'
self.messages = await self.create_msg(room=self.room_name)
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
await self.send(text_data=json.dumps({
'db_messages': self.messages,
}))
async def disconnect(self, close_code):
print(close_code)
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
type = text_data_json['type']
message = text_data_json['message']
username = text_data_json['username']
user_id = text_data_json['user_id']
self.user_id = text_data_json['user_id']
if type == 'chatroom_message':
self.msg = await self.create_msg(user_id, message, self.room_name)
await self.channel_layer.group_send(
self.room_group_name, {
'type': type,
'message': message,
'username': username,
'user_id': user_id
}
)
async def chatroom_message(self, event):
message = event['message']
username = event['username']
await self.send(text_data=json.dumps({
'message': message,
'username': username,
}))
# nefunkcni ukazatel momentalnich uzivatelu v mistnosti
async def get_user(self, event):
print('get_user called')
if event['message'] == 'disconnect':
print('remove', event['username'])
try:
self.users.remove(event['username'])
except ValueError:
print('user already removed')
else:
if event['username'] not in self.users:
self.users.append(event['username'])
print(self.users)
await self.send(text_data=json.dumps({
'users': self.users
}))
При таком подходе он корректно показывает текущих залогиненных пользователей в представлении только от 1-го пользователя, который вошел в чат, другие пользователи не видят, что пользователь, который присоединился до них, находится там. В этом подходе я определяю переменную users в конструкторе.
Consumers.py - подход 2
При таком подходе пользователи отображаются правильно, но не из текущей комнаты (отображаются пользователи из всех других комнат). Это вроде бы логично, потому что объявление переменной находится на верхнем уровне.
Но мой вопрос в том, где я должен объявить его тогда? Когда он находится в конструкторе, он всегда перезаписывает пользователей, которые были в комнате до текущего пользователя, а когда он находится на верхнем уровне, он берет всех пользователей из всех комнат.
Если вы еще не нашли ответа, вот мои 2 цента:
Потребители являются синглтонами, то есть для каждого канала (websocket-соединения) существует один экземпляр. Так что, вероятно, в этом и заключается проблема наличия users внутри класса потребителя.
Альтернативой может быть создание словаря, а не массива. Таким образом, вы можете иметь ключ для каждой комнаты, например:
users = { "room_1": [], "room_2": [] }
Я знаю, что в моем ответе нет кода, но я надеюсь, что он послужит руководством для вашего решения!