Невозможно сохранить сообщения чата с помощью django и websocket
Я следовал этому руководству, чтобы создать функцию чата между двумя пользователями, используя Django и WebSockets/Channels: https://www.youtube.com/watch?v=lHGcSOkrKw0&t=122s&ab_channel=AaravTech
Все работает нормально, кроме сохранения сообщений чата. Когда я обновляю страницу, чат исчезает. При проверке базы данных экземпляр потока создается, но в него ничего не сохраняется. Это просто пустой {}. Экземпляр потока может быть вызван и имеет ID. Когда я пытаюсь соединиться с пользователем, с которым я уже соединился, новый экземпляр потока не создается
app_chat/managers.py
from django.db import models
from django.db.models import Count
class ThreadManager(models.Manager):
def get_or_create_personal_thread(self, user1, user2):
threads = self.get_queryset().filter(thread_type='personal')
threads = threads.filter(users__in=[user1, user2]).distinct()
threads = threads.annotate(u_count=Count('users')).filter(u_count=2)
if threads.exists():
return threads.first()
else:
thread = self.create(thread_type='personal')
thread.users.add(user1)
thread.users.add(user2)
return thread
def by_user(self, user):
return self.get_queryset().filter(users__in=[user])
app_chat/models.py
from django.db import models
from app_users.models import User
from app_chat.managers import ThreadManager
class TrackingModel(models.Model):
created_on = models.DateTimeField(auto_now_add=True)
updated_on = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Thread(TrackingModel):
THREAD_TYPE = (
('personal', 'Personal'),
('group', 'Group')
)
name = models.CharField(max_length=50, null=True, blank=True)
thread_type = models.CharField(
max_length=15, choices=THREAD_TYPE, default='group')
users = models.ManyToManyField(User)
objects = ThreadManager()
def __str__(self):
if self.thread_type == 'personal' and self.users.count() == 2:
return f'{self.users.first()} and {self.users.last()}'
return f'{self.name}'
class Message(TrackingModel):
thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
sender = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField(blank=False, null=False)
def __str__(self) -> str:
return f'From <Thread - {self.thread}>'
app_chat/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from app_users.models import User
from app_chat.models import Thread, Message
from asgiref.sync import sync_to_async
from channels.db import database_sync_to_async
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
me = self.scope['user']
other_username = self.scope['url_route']['kwargs']['username']
other_user = await sync_to_async(User.objects.get)(username=other_username)
self.thread_obj = await sync_to_async(Thread.objects.get_or_create_personal_thread)
(me, other_user)
self.room_name = f'personal_thread_{self.thread_obj.id}'
self.room_group_name = 'chat_%s' % self.room_name
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
print(f"Connected to server == {self.thread_obj}")
async def disconnect(self, close_code):
# Leave group
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
username = text_data_json['username']
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message,
'username': username,
}
)
print(f'{username} : {message} Message recieved')
await self.store_message(message)
# Receive message from room group
async def chat_message(self, event):
message = event['message']
username = event['username']
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message,
'username': username,
}))
print(f'{username} : {message} Message recieved')
@database_sync_to_async
def store_message(self, message):
new_message = Message.objects.create(
thread=self.thread_obj,
sender=self.scope['user'],
text=message
)
print(f'Message stored')
new_message.save()
Я думаю, что проблема может быть связана с self.thread_obj в файле consumper.py. Когда я добавляю print(f "Connected to server == {self.thread_obj}"), я получаю ошибку, описанную ниже. Без print ошибки нет, а сохранение все равно не работает.
HTTP GET /chat/mickeykbp/ 200 [0.05, 127.0.0.1:50331]
WebSocket HANDSHAKING /ws/chat/mickeykbp/ [127.0.0.1:50333]
Exception inside application: object SyncToAsync can't be used in 'await' expression
Traceback (most recent call last):
File "C:\Users\Anouphong\.virtualenvs\project_homecook-l563S6IX\lib\site-packages\channels\staticfiles.py", line 44, in __call__
return await self.application(scope, receive, send)
File "C:\Users\Anouphong\.virtualenvs\project_homecook-l563S6IX\lib\site-packages\channels\routing.py", line 71, in __call__
return await application(scope, receive, send)
File "C:\Users\Anouphong\.virtualenvs\project_homecook-l563S6IX\lib\site-packages\channels\sessions.py", line 47, in __call__
return await self.inner(dict(scope, cookies=cookies), receive, send)
File "C:\Users\Anouphong\.virtualenvs\project_homecook-l563S6IX\lib\site-packages\channels\sessions.py", line 263, in __call__
return await self.inner(wrapper.scope, receive, wrapper.send)
File "C:\Users\Anouphong\.virtualenvs\project_homecook-l563S6IX\lib\site-packages\channels\auth.py", line 185, in __call__
return await super().__call__(scope, receive, send)
File "C:\Users\Anouphong\.virtualenvs\project_homecook-l563S6IX\lib\site-packages\channels\middleware.py", line 26, in __call__
return await self.inner(scope, receive, send)
File "C:\Users\Anouphong\.virtualenvs\project_homecook-l563S6IX\lib\site-packages\channels\routing.py", line 150, in __call__
return await application(
File "C:\Users\Anouphong\.virtualenvs\project_homecook-l563S6IX\lib\site-packages\channels\consumer.py", line 94, in app
return await consumer(scope, receive, send)
File "C:\Users\Anouphong\.virtualenvs\project_homecook-l563S6IX\lib\site-packages\channels\consumer.py", line 58, in __call__
await await_many_dispatch(
File "C:\Users\Anouphong\.virtualenvs\project_homecook-l563S6IX\lib\site-packages\channels\utils.py", line 51, in await_many_dispatch
await dispatch(result)
File "C:\Users\Anouphong\.virtualenvs\project_homecook-l563S6IX\lib\site-packages\channels\consumer.py", line 73, in dispatch
await handler(message)
File "C:\Users\Anouphong\.virtualenvs\project_homecook-l563S6IX\lib\site-packages\channels\generic\websocket.py", line 173, in websocket_connect
await self.connect()
File "C:\Users\Anouphong\Desktop\coding\project_homecook\project_homecook\app_chat\consumers.py", line 15, in connect
self.thread_obj = await sync_to_async(Thread.objects.get_or_create_personal_thread)
TypeError: object SyncToAsync can't be used in 'await' expression
WebSocket DISCONNECT /ws/chat/mickeykbp/ [127.0.0.1:50333]