# Django Channels配置记录
5 min read
Table of Contents
官方文档:https://channels.readthedocs.io/en/latest/tutorial/index.html
django版本:4.1
channels版本:3.0.5
一、安装各种包&创建APP
1.安装channels、channels-redis(用于验证连接redis)
2.安装Redis Vers.>=5,小于5会报错unknown command ‘BZPOPMIN’
windows上的redis地址
redis-x64-50141.zip
12MB
# 使用帮助:1.注册到服务:redis-server --service-install redis.windows.conf2.进入“服务”,找到Redis,右键点击开启
Redis使用:卸载服务:redis-server --service-uninstall开启服务:redis-server --service-start停止服务:redis-server --service-stop
# 测试连接(需安装channels-redis),打开pycharm里的python控制台:>>> import channels.layers>>> channel_layer = channels.layers.get_channel_layer()>>> from asgiref.sync import async_to_sync>>> async_to_sync(channel_layer.send)('test_channel', {'type': 'hello'})>>> async_to_sync(channel_layer.receive)('test_channel'){'type': 'hello'}3.新建名字叫chat的app:python manage.py startapp chat
二、配置settings.py
1.配置INSTALLED_APPS
INSTALLED_APPS = [ "channels", "chat", 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles',]2.配置ASGI_APPLICATION 和Redis
# settings.py最后面# ChannelsASGI_APPLICATION = 'djangoProject.asgi.application'CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels_redis.core.RedisChannelLayer', 'CONFIG': { "hosts": [('127.0.0.1', 6379)], }, },}三、配置asgi.py
(django2.2以下木有自带的asgi.py,需要自建,且要django.setup())
import os
from channels.auth import AuthMiddlewareStackfrom channels.routing import ProtocolTypeRouter, URLRouterfrom channels.security.websocket import AllowedHostsOriginValidatorfrom django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoProject.settings')# Initialize Django ASGI application early to ensure the AppRegistry# is populated before importing code that may import ORM models.django_asgi_app = get_asgi_application()
import chat.routing
application = ProtocolTypeRouter({ "http": get_asgi_application(), # 使用chat下的routing来处理websocket "websocket": AllowedHostsOriginValidator( AuthMiddlewareStack( URLRouter( chat.routing.websocket_urlpatterns ) ) ),})配置好后,需要执行migrate:python manage.py migrate
四、配置templates
1.配置index.html
<!DOCTYPE html><html><head> <meta charset="utf-8"/> <title>Chat Rooms</title></head><body> What chat room would you like to enter?<br> <input id="room-name-input" type="text" size="100"><br> <input id="room-name-submit" type="button" value="Enter">
<script> document.querySelector('#room-name-input').focus(); document.querySelector('#room-name-input').onkeyup = function(e) { if (e.keyCode === 13) { // enter, return document.querySelector('#room-name-submit').click(); } };
document.querySelector('#room-name-submit').onclick = function(e) { var roomName = document.querySelector('#room-name-input').value; window.location.pathname = '/chat/' + roomName + '/'; }; </script></body></html>2.配置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'); };
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>五、配置urls.py和routing.py
1.配置主urls.py
from django.contrib import adminfrom django.urls import path, include
urlpatterns = [ path('admin/', admin.site.urls), path("chat/", include("chat.urls")),]2.配置chat里的urls.py
from django.urls import pathfrom . import viewsurlpatterns = [ path('', views.index, name='index'), path('<str:room_name>/', views.room, name='room'), # str后不要有空格]3.新建routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [ re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),]六、配置view.py和consumers.py
1.配置views.py
from django.shortcuts import render
def index(request): return render(request, 'chat/index.html', {})
def room(request, room_name): return render(request, 'chat/room.html', { 'room_name': room_name })2.新建consumers.py(处理ws请求的核心)
# —————————————————————————————这里为同步代码(官方文档第2部分)—————————————————————————————# import json# from asgiref.sync import async_to_sync# from channels.generic.websocket import WebsocketConsumer### class ChatConsumer(WebsocketConsumer):# def connect(self):# self.room_name = self.scope['url_route']['kwargs']['room_name']# self.room_group_name = 'chat_%s' % self.room_name## # Join room group# async_to_sync(self.channel_layer.group_add)(# self.room_group_name,# self.channel_name# )## self.accept()## def disconnect(self, close_code):# # Leave room group# async_to_sync(self.channel_layer.group_discard)(# self.room_group_name,# self.channel_name# )## # Receive message from WebSocket# def receive(self, text_data):# text_data_json = json.loads(text_data)# message = text_data_json['message']## # Send message to room group# async_to_sync(self.channel_layer.group_send)(# self.room_group_name,# {# 'type': 'chat_message',# 'message': message# }# )## # Receive message from room group# def chat_message(self, event):# message = event['message']## # Send message to WebSocket# self.send(text_data=json.dumps({# 'message': message# }))
# —————————————————————————————以下为异步代码(官方文档第3部分)—————————————————————————————
import jsonfrom channels.generic.websocket import AsyncWebsocketConsumer
class ChatConsumer(AsyncWebsocketConsumer): """ Differences: ChatConsumer now inherits from AsyncWebsocketConsumer rather than WebsocketConsumer. All methods are async def rather than just def. await is used to call asynchronous functions that perform I/O. async_to_sync is no longer needed when calling methods on the channel layer. """ async def connect(self): self.room_name = self.scope['url_route']['kwargs']['room_name'] 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()
async def disconnect(self, close_code): # Leave room 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']
# Send message to room group await self.channel_layer.group_send( self.room_group_name, { 'type': 'chat_message', 'message': message } )
# Receive message from room group async def chat_message(self, event): message = event['message']
# Send message to WebSocket await self.send(text_data=json.dumps({ 'message': message }))