Django Cookiecutter Channels3 - соединение открывается, но не отправляется
Я начал проект с Django Cookiecutter с Docker: https://cookiecutter-django.readthedocs.io/en/latest/
Я пытаюсь добавить каналы и следую руководству в их документации: https://channels.readthedocs.io/en/stable/tutorial
Я добавил Channels 3.0.4 в requirements.txt, пересобрал docker контейнер.
Я добавил каналы в settings/base.py, и вот что:
WSGI_APPLICATION = "config.wsgi.application"
ASGI_APPLICATION = "config.asgi.application"
Я обновил свой файл config/asgi.py:
import os
import sys
from pathlib import Path
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from the_pub.chat import routing
ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent
sys.path.append(str(ROOT_DIR / "the_pub"))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
django_application = get_asgi_application()
from config.websocket import websocket_application # noqa isort:skip
application = ProtocolTypeRouter({
"https": django_application,
"websocket": AuthMiddlewareStack(
URLRouter(
routing.websocket_urlpatterns
)
),
})
async def application(scope, receive, send):
if scope["type"] == "http":
await django_application(scope, receive, send)
elif scope["type"] == "websocket":
await websocket_application(scope, receive, send)
else:
raise NotImplementedError(f"Unknown scope type {scope['type']}")
создан файл config/websocket.io
async def websocket_application(scope, receive, send):
while True:
event = await receive()
if event["type"] == "websocket.connect":
await send({"type": "websocket.accept"})
if event["type"] == "websocket.disconnect":
break
if event["type"] == "websocket.receive":
if event["text"] == "ping":
await send({"type": "websocket.send", "text": "pong!"})
просмотров:
# chat/views.py
from django.shortcuts import render
def index(request):
return render(request, 'chat/index.html')
def index(request):
return render(request, 'chat/index.html', {})
def room(request, room_name):
return render(request, 'chat/room.html', {
'room_name': room_name
})
маршрутизация:
# chat/routing.py
from django.urls import re_path
from the_pub.chat import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
чат/урлы:
# chat/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('<str:room_name>/', views.room, name='room'),
]
потребитель:
# chat/consumers.py
import json
from channels.generic.websocket import WebsocketConsumer
class ChatConsumer(WebsocketConsumer):
def connect(self):
self.accept()
def disconnect(self, close_code):
pass
def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
self.send(text_data=json.dumps({
'message': message
}))
app.py
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class ChatConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'the_pub.chat'
verbose_name= _("Chat")
def ready(self):
try:
import the_pub.users.signals # noqa F401
except ImportError:
pass
template:
<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Chat Room</title>
</head>
<body>
<textarea id="chat-log" cols="100" rows="20"></textarea><br>
<input id="chat-message-input" type="text" size="100"><br>
<input id="chat-message-submit" type="button" value="Send">
{{ room_name|json_script:"room-name" }}
<script>
const roomName = JSON.parse(document.getElementById('room-name').textContent);
const chatSocket = new WebSocket(
'ws://'
+ window.location.host
+ '/ws/chat/'
+ roomName
+ '/'
);
chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
document.querySelector('#chat-log').value += (data.message + '\n');
console.log(data);
};
chatSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function(e) {
if (e.keyCode === 13) { // enter, return
document.querySelector('#chat-message-submit').click();
}
};
document.querySelector('#chat-message-submit').onclick = function(e) {
const messageInputDom = document.querySelector('#chat-message-input');
const message = messageInputDom.value;
chatSocket.send(JSON.stringify({
'message': message
}));
messageInputDom.value = '';
};
</script>
</body>
</html>
Когда я тестирую вебсокет в плагине chrome, я могу отправлять сообщения и они записываются в журнал. Когда я нажимаю кнопку "Отправить" на форме, ничего не происходит. Ни консольных предупреждений, ни ничего в журналах docker. Все, что он делает, это очищает текст в текстовом поле. Я не думал, что третья сторона может проверить сокет, потому что я обернул его в слой аутентификации, но все наоборот, мое приложение ведет себя так, как будто javascript для отправки сообщения в сокет не существует.
При установке Channels говорится, что нужно сделать 'pip -m install -U channels'. Я добавил каналы в базовый файл requirements.txt и позволил django cookiecutter запустить установку с остальными библиотеками. это сломало его?
Также, я запускаю этот проект, он был установлен с помощью cookiecutter, который, я полагаю, является wsgi. Возможно ли вообще так использовать wsgi и asgi или мне следует искать способ запустить весь сайт на asgi?
В консоли появляется ошибка "DevTools failed to load source map: Could not load content for /requestProvider.js.map". Обычно я игнорирую эти ошибки, но эта подозрительно связана с функцией socket.send() при срабатывании .onmessage.
Вы должны убрать
async def application(...)изconfig/asgi.py, поскольку вы определяете приложение как переменную прямо выше.При использовании каналов, файл
config/websocket.pyне совсем нужен, так как вы обрабатываете websocket соединения от потребителей.Убедитесь, что channels_redis установлен, если вы используете Redis для канального уровня.
Ваш
.chat/views.pyимеет два индексных представления. Не уверен, что так и задумано, но это создаст ненужную путаницу, когда это не будет работать.