Can't establish connection between web socket and server
I'm writing a chat app using django channels, redis and daphne. When I try to submit the message and tap on the button I have GET query, but I've written js in my html file and it has to prevent standart behaviour of this form. I don't know why doesn't it establish the connection and just send GET query, what's wrong here? I've checked redis server, It works. I've installed all required modules, I've checked It with gpt, nothing helps. How to make it establish the connection?
consumer.py:
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from django.apps import apps
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.chat_name = self.scope['url_route']['kwargs']['chat_name']
await self.channel_layer.group_add(self.chat_name, self.channel_name)
await self.accept()
@database_sync_to_async
def save_message(self, text_data):
Chat = apps.get_model('chat', 'Chat')
ChatMessage = apps.get_model('chat', 'ChatMessage')
User = apps.get_model('auth', 'User')
chat = Chat.objects.get(chat_name=text_data['chat_name'])
sender = User.objects.get(username=text_data['sender'])
ChatMessage.objects.create(
chat=chat,
body=text_data['message'],
sender = sender,
)
async def receive(self, text_data):
print('Received from frontend:', text_data)
data_json = json.loads(text_data)
await self.save_message(data_json)
event = {"type": "send_chat_message", "message": data_json}
await self.channel_layer.group_send(self.chat_name, event)
async def send_chat_message(self, event):
await self.send(text_data=json.dumps({"message": event["message"]}))
chat.html:
{% extends "base.html" %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chat</title>
</head>
<body>
{% block content %}
<div class="container">
<h2>Chat with {{ chat.members.all.0 }}</h2>
<div class="card">
<div class="card-body" id="message-container" style="height: 300px; overflow-y: scroll;">
{% for message in chat_messages %}
<p><strong>{{ message.sender.username }}</strong>: {{ message.body }}</p>
{% empty %}
<p>There are no meassages yet...</p>
{% endfor %}
</div>
</div>
<form action="" id="message-form" name='message-form'>
<input name="message-input" id="message-input"></input>
<button type="submit">Send</button>
</form>
</div>
{% endblock %}
<script>
const chat_name = "{{ chat.chat_name }}"
const socket = new WebSocket(`ws://127.0.0.1:8000/ws/chat/${chat_name}/`) // creatimg ws connection
// sending the message to the socket
const message_form = document.getElementById("message-form")
message_form.addEventListener( // Listener for submit button
"submit", function (event) {
event.preventDefault()
let sent_message = document.getElementById("message-input").value // get the value of the input of the form
socket.send(
JSON.stringify({
message: sent_message,
chat_name: "{{ chat.chat_name }}",
sender: "{{ request.user.username }}"
})
)
}
)
//Getting the message
const chats_div = document.getElementById("message-container") // search for container where we will paste the message
function scrollToBottom() {
chats_div.scrollTop = chats_div.scrollHeight
} //???
//Processing of the message from the server
socket.addEventListener("message", function(e) {
const data = JSON.parse(e.data);
let sender = data.message.sender
let content = data.message.message
let msg_body = document.createElement("div")
let msg_sender = document.createElement("div")
msg_body.textContent = content
msg_sender.textContent = sender
let chats_div = document.getElementById("message-container")
let msg = document.createElement("p")
msg.innerHTML = `<strong>${msg_sender.textContent}</strong>: ${msg_body.textContent}`;
chats_div.appendChild(msg);
scrollToBottom()
})
</script>
</body>
</html>
asgi.py:
import os
import django
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from chat.routing import websocket_urlpatterns
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'messanger.settings')
django.setup()
application = ProtocolTypeRouter({
"http": get_asgi_application(), # Обычные HTTP-запросы
"websocket": AuthMiddlewareStack(URLRouter(websocket_urlpatterns)),
})
routings.py:
from django.urls import re_path
from .consumers import ChatConsumer
websocket_urlpatterns = [
re_path(r"ws/chat/(?P<chat_name>\w+)/$", ChatConsumer.as_asgi()),
]
views.py
from django.shortcuts import render
from .models import *
def chat_list(req):
chats = req.user.chats.all()
print(chats)
return render(req, 'chat_list.html', {'chats': chats})
def chat(req, chat_name):
chat = req.user.chats.get(chat_name=chat_name)
chat_messages = ChatMessage.objects.filter(chat=chat)
return render(req, 'chat.html', {'chat_messages': chat_messages, 'chat': chat})
models.py:
from django.db import models
from django.contrib.auth.models import User
class Chat(models.Model):
chat_name = models.CharField(max_length=50)
members = models.ManyToManyField(User, related_name='chats')
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.chat_name
class ChatMessage(models.Model):
body = models.TextField()
chat = models.ForeignKey(Chat, on_delete=models.CASCADE, related_name='chat')
sender = models.ForeignKey(User, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"Message by @{self.sender} from the chat '{self.chat.chat_name}'"
I've just solved it. I forgot about script block in html file. It is subsidiary htmll, so we have to put {% block scripts %}
here before <script>
tag. Also It is required to put {% extends "base.html" %}
inside body.