Used the Channels documentation to create these notes Channels Documentation.
Follow the Channels documentation's tutorial for a fuller walkthrough.
NOTE: This version of Channels only works with Python 3.5 or higher!
-
Install Redis. this will handle the transactions between the websockets.
sudo apt-get install redis
-
Activate the project virtual environment and use pip to install.
pip install -U channels && pip install channels-redis
-
Add channels to the project settings
INSTALLED_APPS
.settings.py
'channels'
-
Before closing the project settings add the following config for the channels layer.
settings.py
# Channels Settings ASGI_APPLICATION = '<project-name>.routing.application' CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels_redis.core.RedisChannelLayer', 'CONFIG': { "hosts": [('127.0.0.1', 6379)], }, }, }
-
From the project folder (same folder as settings.py & wsgi.py) create a routing.py file and setup the main router. The router is comparable to a standard Django view for websockets. The code with the single hash can be modified once the app routing.py has been created.
<project>/routing.py
from channels.auth import AuthMiddlewareStack from channels.routing import ProtocolTypeRouter, URLRouter # used to setup application routing import <app-name>.routing # used to import the application router application = ProtocolTypeRouter({ # HTTP django views is added by default 'websocket': AuthMiddlewareStack( # for application routing, added using URLRouter import above in example below. URLRouter( <app-name>.routing.websocket_urlpatterns ) ), })
-
On the HTML page you want to create the websocket, using javascript to create the websocket. This example creates a websocket for a basic chat room. This will still need a URL and view to route to the page accordingly.
{{html_template}}.html
// Creates a new websocket as a var named 'chatSocket' var chatSocket = new WebSocket('ws://' + window.location.host + '/ws/chat/'); // Receives messages from server via websocket and displays them in the 'chat-log' chatSocket.onmessage = function(e){ var data = JSON.parse(e.data); var user = data['user']; var time = data['time']; var message = data['message']; document.querySelector('#chat-log').value += (user + ' ' + time + '\n' + message + '\n\n'); scrollToBottom() }; // JS for input box 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(); } }; // Takes the input from the 'chat-message-input' and sends as JSON dict document.querySelector('#chat-message-submit').onclick = function(e) { var messageInputDom = document.querySelector('#chat-message-input'); var user = "{{ request.user }}"; var time = getTime(); var message = messageInput.value; var jstring = JSON. stringify({ 'user': user, 'time': time, 'message': message }); chatSocket.send(jstring); messageInputDom.value = ''; }; function scrollToBottom(){ document.getElementById("chat-log").scrollTop = document.getElementById("chat-log").scrollHeight; }
-
Create the application routing to route the websocket to the consumer. You would also want to un-comment the code from the main router or add as needed. All app routers need to be routed to the main router which is routed to Redis for brokering.
<app>/routing.py
from django.urls import path from . import consumers websocket_urlpatterns = [ path('ws/chat/', consumers.ChatConsumer), ]
-
Create the consumer which responds to the interaction to javascript from the HTML page.
chat/consumers.py
from asgiref.sync import async_to_sync from channels.generic.websocket import WebsocketConsumer import json class ChatConsumer(WebsocketConsumer): # Connects to the websocket and joins a group (broadcast) with the static name 'chat' def connect(self): async_to_sync(self.channel_layer.group_add)("chat", self.channel_name) self.accept() # Disconnects from the websocket and broadcast group with the static name 'chat' def disconnect(self, close_code): async_to_sync(self.channel_layer.group_discard)("chat", self.channel_name) # Receive message from WebSocket def receive(self, text_data): get_key_val = json.loads(text_data) user = get_key_val['user'] time = get_key_val['time'] message = get_key_val['message'] # Send message to room group async_to_sync(self.channel_layer.group_send)( "chat", { "type": "chat_message", "user": user, "time": time, "message": message } ) # Receive message from room group def chat_message(self, event): user = event["user"] time = event["time"] message = event["message"] # Send message to WebSocket self.send(text_data=json.dumps({ "user": user, "time": time, "message": message }))
NOTE: you can make the process completely asynchronous use the link to see how.
[group:chat]
programs = chat_gunicorn, chat_daphne
priority = 999
[program:chat_gunicorn]
directory = /home/joswar/projects/mysite/
command = /home/joswar/envs/mysite/bin/gunicorn -c /home/joswar/projects/mysite/conf/gunicorn.py mysite.wsgi:application
autostart = true
autorestart = true
user = joswar
priority = 990
stopsignal = KILL
[program:chat_daphne]
directory = /home/joswar/projects/mysite/
command = /home/joswar/envs/mysite/bin/daphne --root-path=/home/joswar/projects/mysite/ mysite.asgi:application -p 8086
autostart = true
autorestart = true
user = joswar
priority = 990
stopsignal = KILL
server {
server_name 10.6.0.84;
listen 84;
location /static {
alias /home/joswar/projects/mysite/static/;
autoindex on;
}
location /media {
alias /home/joswar/projects/mysite/media/;
autoindex on;
}
location /ws {
proxy_pass http://127.0.0.1:8086;
proxy_redirect off;
proxy_http_version 1.1;
proxy_read_timeout 86400;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
proxy_pass http://127.0.0.1:8084;
proxy_redirect off;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}