Ошибка Websocket-соединения в Django и ошибка "Пользователь, соответствующий запросу, не существует
Я пытаюсь создать приложение для чата один на один, используя django channels, websockets и Redis.Пробовал реализовать django channels из (https://youtu.be/RVH05S1qab8), но получаю ошибки вроде этой (из консоли javaScript).Строка 253 из views.py, код которой приведен ниже.Также ошибка в строке 95 models.py, которая также приведена в коде ниже.
Также сообщение не отправляется другому пользователю в реальном времени, и страницу приходится обновлять, чтобы получить сообщение.
Код для thread.html:
{% extends "bookexchange/base.html" %}
{% block content %}
<h3>Thread for {% if user != object.first %}{{ object.first }}{% else %}{{ object.second }}{% endif %}</h3>
<ul id='chat-items'>
{% for chat in object.chatmessage_set.all %}
<li>{{ chat.message }} via {{ chat.user }}</li>
{% endfor %}
</ul>
<form id='form' method='POST'> {% csrf_token %}
<input type="hidden" id="myUsername" value="{{ user.username }}">
{{form.as_p }}
<input type='submit' class='btn btn-primary'/>
</form>
{% endblock %}
{% block script %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/reconnecting-websocket/1.0.0/reconnecting-websocket.min.js" integrity="sha512-B4skI5FiLurS86aioJx9VfozI1wjqrn6aTdJH+YQUmCZum/ZibPBTX55k5d9XM6EsKePDInkLVrN7vPmJxc1qA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
var loc=window.location
var formData=$("#form")
var msgInput=$("#id_message")
var chatHolder=$("#chat-items")
var me=$("#myUsername").val()
var wsStart='ws://'
if(loc.protocol=='https:'){
wsStart='wss://'
}
var endpoint =wsStart + loc.host + loc.pathname
var socket= new WebSocket(endpoint)
socket.onmessage=function(e){
console.log("message",e)
var chatDataMsg=JSON.parse(e.data)
chatHolder.append("<li>"+ chatDataMsg.message +"via"+ chatDataMsg.username +"</li>")
}
socket.onopen=function(e){
console.log("open",e)
formData.submit(function(event){
event.preventDefault()
var msgText=msgInput.val()
// chatHolder.append("<li>"+ msgText + " via "+me + "</li>")
// var formDataSerialized=formData.serialize()
var finalData={
'message': msgText
}
socket.send(JSON.stringify(finalData))
// msgInput.val('')
formData[0].reset()
})
}
socket.onerror=function(e){
console.log("error",e)
}
socket.onclose=function(e){
console.log("close",e)
}
</script>
{% endblock %}
Код для consumers.py:
import asyncio
import json
from django.contrib.auth import get_user_model
from channels.consumer import AsyncConsumer
from channels.db import database_sync_to_async
from .models import Thread,ChatMessage
class ChatConsumer(AsyncConsumer):
async def websocket_connect(self,event):
print("connected",event)
other_user=self.scope['url_route']['kwargs']['username']
me = self.scope['user']
# print(other_user,me)
thread_obj=await self.get_thread(me,other_user)
print(me,thread_obj.id)
self.thread_obj=thread_obj
chat_room=f"thread_{thread_obj.id}"
self.chat_room=chat_room
await self.channel_layer.group_add(
chat_room,
self.channel_name
)
await self.send({
'type':"websocket.accept"
})
# await asyncio.sleep(10)
async def websocket_receive(self,event):
print("receive",event)
front_text=event.get('text',None)
if front_text is not None:
loaded_dict_data=json.loads(front_text)
msg=loaded_dict_data.get('message')
user=self.scope['user']
username='default'
if user.is_authenticated:
username=user.username
myResponse={
'message':msg,
'username':username
}
await self.create_chat_message(user,msg)
await self.channel_layer.group_send(
self.chat_room,
# new_event
{
'type':"chat_message",
"text":json.dumps(myResponse)
}
)
async def chat_message(self,event):
print('message',event)
await self.send({
'type':"chat_message",
"text":event['text']
})
async def websocket_disconnect(self,event):
print("disconnected",event)
@database_sync_to_async
def get_thread(self,user,other_username):
return Thread.objects.get_or_new(user,other_username)[0]
@database_sync_to_async
def create_chat_message(self,me,msg):
thread_obj=self.thread_obj
me=self.scope['user']
return Thread.objects.create(thread=thread_obj,user=me,message=msg)
Код для views.py:
class ThreadView(LoginRequiredMixin, FormMixin, DetailView):
template_name = 'bookexchange/thread.html'
form_class = ComposeForm
success_url = './'
def get_queryset(self):
return Thread.objects.by_user(self.request.user)
def get_object(self):
other_username = self.kwargs.get("username")
obj, created = Thread.objects.get_or_new(self.request.user,other_username) #line 253
if obj == None:
raise Http404
return obj
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'] = self.get_form()
return context
Models.py содержит модель User, созданную с помощью AbstractUser. Также он содержит функцию 'broadcast_msg_to_chat', которая не определена нигде в исходном коде. Код имеет следующий вид
class ThreadManager(models.Manager):
def by_user(self, user):
qlookup = Q(first=user) | Q(second=user)
qlookup2 = Q(first=user) & Q(second=user)
qs = self.get_queryset().filter(qlookup).exclude(qlookup2).distinct()
return qs
def get_or_new(self, user, other_username): # get_or_create
username = user.username
if username == other_username:
return None
qlookup1 = Q(first__username=username) & Q(second__username=other_username)
qlookup2 = Q(first__username=other_username) & Q(second__username=username)
qs = self.get_queryset().filter(qlookup1 | qlookup2).distinct()
if qs.count() == 1:
return qs.first(), False
elif qs.count() > 1:
return qs.order_by('timestamp').first(), False
else:
Klass = user.__class__
user2 = Klass.objects.get(username=other_username) #line 95
if user != user2:
obj = self.model(
first=user,
second=user2
)
obj.save()
return obj, True
return None, False
class Thread(models.Model):
first = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='chat_thread_first')
second = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='chat_thread_second')
updated = models.DateTimeField(auto_now=True)
timestamp = models.DateTimeField(auto_now_add=True)
objects = ThreadManager()
@property
def room_group_name(self):
return f'chat_{self.id}'
def broadcast(self, msg=None):
if msg is not None:
broadcast_msg_to_chat(msg, group_name=self.room_group_name, user='admin')
return True
return False
class ChatMessage(models.Model):
thread = models.ForeignKey(Thread, null=True, blank=True, on_delete=models.SET_NULL)
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='sender', on_delete=models.CASCADE)
message = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
Код для routing.py:
application= ProtocolTypeRouter({
'websocket':AllowedHostsOriginValidator(
AuthMiddlewareStack(
URLRouter(
[
re_path(r"^messages/(?P<username>[\w.@+-]+)/$", ChatConsumer),
]
)
)
)
})