Skip to content

Instantly share code, notes, and snippets.

@jgram925
Last active June 15, 2019 17:51
Show Gist options
  • Save jgram925/9b0ad86acacf667792b0bb90fcee1ae8 to your computer and use it in GitHub Desktop.
Save jgram925/9b0ad86acacf667792b0bb90fcee1ae8 to your computer and use it in GitHub Desktop.
Django Channels 2.0 Setup.md

Channels Installation & Setup

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!

  1. Install Redis. this will handle the transactions between the websockets.

     sudo apt-get install redis
    
  2. Activate the project virtual environment and use pip to install.

     pip install -U channels && pip install channels-redis
    
  3. Add channels to the project settings INSTALLED_APPS.

    settings.py

     'channels'
    
  4. 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)],
             },
         },
     }
    
  5. 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
             )
         ),
     })
    

Create the Websocket & Consumer

  1. 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;
     }
    
  2. 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),
     ]
    
  3. 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.

LINK TO ASYNC TUTORIAL

Supervisor Configuration with Gunicorn & Daphne

[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

Nginx Configuration with Gunicorn & Daphne

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;
    }   
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment