问题
I am following a turtorial Django Channel 2, I followed all steps and i am having a failed connection in console. I noticed this problem occur when i add Channel_layer to settings, when i remove Channel_layer the connection becomes successful. How do i solve this problem?
Manager
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)
if user != user2:
obj = self.model(
first=user,
second=user2
)
obj.save()
return obj, True
return None, False
Model.py
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=True,null=True)
profile_pic = models.ImageField(upload_to='ProfilePicture/', default="ProfilePicture/avatar.png", blank=True)
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)
Views.py
class InboxView(LoginRequiredMixin, ListView):
template_name = 'inbox.html'
def get_queryset(self):
return Thread.objects.by_user(self.request.user)
class ThreadView(LoginRequiredMixin, FormMixin, DetailView):
template_name = '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)
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
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
thread = self.get_object()
user = self.request.user
message = form.cleaned_data.get("message")
ChatMessage.objects.create(user=user, thread=thread, message=message)
return super().form_valid(form)
urls.py
from django.urls import path
from pixmate.views import (
InboxView,
ThreadView,
)
app_name = 'pixmate'
urlpatterns = [
path("inbox", InboxView.as_view(), name='inbox'),
path('messages/<username>/', ThreadView.as_view(), name='messages'),
]
asgi.py
import os
import django
from channels.routing import get_default_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pixmateDjango.settings")
django.setup()
application = get_default_application()
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 pixmate.models import Thread, ChatMessage
from django.db.models import Q
class ChatConsumer(AsyncConsumer):
async def websocket_connect(self, event):
print('connected', event)
other_user = self.scope['url_route']['kwargs']['username']
me = self.scope['user']
thread_obj = await self.get_thread(me, other_user)
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"
})
async def websocket_receive(self, event):
# when a message is recieved from the websocket
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)
# broadcasts the message event to be sent, the group send layer
# triggers the chat_message function for all of the group (chat_room)
await self.channel_layer.group_send(
self.chat_room,
{
"type": "chat_message",
"text": json.dumps(myResponse)
}
)
# chat_method is a custom method name that we made
async def chat_message(self, event):
#sends the actual message
await self.send({
"type": "websocket.send",
"text": event['text']
})
async def websocket_disconnect(self, event):
# when the socket disconnects
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
return ChatMessage.objects.create(thread=thread_obj, user=me, message=msg)
routing.py
from django.urls import path
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from channels.security.websocket import AllowedHostsOriginValidator, OriginValidator
from pixmate.consumers import ChatConsumer
application = ProtocolTypeRouter({
# Empty for now (http->django views is added by default)
'websocket': AllowedHostsOriginValidator(
AuthMiddlewareStack(
URLRouter(
[
path('messages/<username>/', ChatConsumer),
]
)
)
)
})
settings.py
ASGI_APPLICATION = "pixmateDjango.routing.application"
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("localhost", 6379)],
},
},
}
Thread.html
<div class="px-4 py-3 chat-box bg-white" id='chat-items' style="height:435px;">
{% for chat in object.chatmessage_set.all %}
{% if request.user != chat.user %}
<!-- Sender Message-->
<div class="media w-50 mb-3">
<img src="{{ chat.user.profile.profile_pic.url }}" id="avatarImg" width="30" height="30" class="rounded-circle mt-1">
<div class="media-body ml-2">
<div class="bg-light rounded py-2 px-3 mb-2">
<p class="text-small mb-0 text-black">{{ chat.message|emoticons }}</p>
</div>
<p class="small text-muted">
{{ chat.timestamp|date:'M j, Y, g:i a' }}
<i class="fa fa-ellipsis-h float-right"></i>
</p>
</div>
</div>
{% endif %}
{% endfor %}
<form id='form' method='POST' action="." class="chatbox-form">
{% csrf_token %}
{{ form }}
<input = "submit" value="send">
</form>
<!-- Channels Reconnecting Websocket -->
<script src='https://cdnjs.cloudflare.com/ajax/libs/reconnecting-websocket/1.0.0/reconnecting-websocket.js'></script>
<script>
// websocket scripts - client side*
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 ReconnectingWebSocket(endpoint)
// below is the message I am receiving
socket.onmessage = function(e) {
console.log("message", e)
var chatDataMsg = JSON.parse(e.data)
var profile_pic = $('#avatarImg').attr("src");
console.log(profile_pic);
//alert(e.data)
if(chatDataMsg.username == me) {
chatHolder.append("<div class='media w-50 ml-auto mb-3'>" + "<div class='media-body'>" + "<div class='bg-primary rounded py-2 px-3 mb-2'>" + "<p class='text-small mb-0 text-white'>" + chatDataMsg.message + "</p>" + "</div>" + "<p class='small text-muted'>" + newMonth + newDate + "<i class='fa fa-ellipsis-h float-right'></i>" + "</p>" + "</div>" + "</div>")
}
else{
chatHolder.append("<div class='media w-50 mb-3'>" + "<img src=" + profile_pic + " width='30' height='30' class='rounded-circle mt-1'/>" + "<div class='media-body ml-2'>" + "<div class='bg-light rounded py-2 px-3 mb-2'>" + "<p class='text-small mb-0 text-black'>" + chatDataMsg.message + "</p>" + "</div>" + "<p class='small text-muted'>" + newMonth + newDate + "<i class='fa fa-ellipsis-h float-right'></i>" + "</p>" + "</div>" + "</div>")
}
}
// below is the message I am sending
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 finalData = {
'message': msgText
}
socket.send(JSON.stringify(finalData))
formData[0].reset()
})
}
socket.onerror = function(e) {
console.log("error", e)
}
socket.onclose = function(e) {
console.log("close", e)
}
</script>
{% endblock %}
回答1:
I figured out the solution to the problem: Redis do not support in windows, so i installed Memurai and it worked.
来源:https://stackoverflow.com/questions/62750983/websocket-disconnected-connect-call-failed