Django Channel and WebSockets

Blogs • Published on 2022-05-19

img

The built-in capabilities of Django are extended with Django Channels, allowing Django projects to handle protocols like WebSockets, MQTT (IoT), chatbots, radios, and other real-time applications in addition to HTTP. It is based on the ASGI Python specification.

Project Setup

$mkdir dhuni-django-channels && cd dhuni-django-channels

$ python -m venv venv

$ venv/Scripts/activate

(venv)$ pip install django==3.2.14

(venv)$ django-admin startproject dashboard

(venv)$ cd dashboard

(venv)$ python manage.py startapp connection

#Application definition

NSTALLED_APPS = [

'apos;django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'connection'

]

Add Channels

(env)$ pip install channels==3.0.5

NSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'connection',

'channels',

]

import os

import django

from channels.routing import get_default_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dashboard.settings')

django.setup()

application = get_default_application()

Adding Channels Consumers

When Django accepts an HTTP request, it consults the root URLconf to lookup a view function, and then calls the view function to handle the request. Similarly, when Channels accepts a WebSocket connection, it consults the root routing configuration to lookup a consumer, and then calls various functions on the consumer to handle events from the connection. However, unlike Django views, consumers are long-running by default. A Django project can have multiple consumers that are combined using Channels routing (which we'll take a look at in the next section).Each consumer has its own scope, which is a set of details about a single incoming connection. They contain pieces of data like protocol type, path, headers, routing arguments, user agent, and more.


                import json
                from asgiref.sync import async_to_sync
                from channels.generic.websocket import JsonWebsocketConsumer
                from channels.layers import get_channel_layer
                class SocketConsumer(JsonWebsocketConsumer):
                    def connect(self):
                        self.room_name = self.scope['url_route']['kwargs']['user_type'].replace('/', '_')
                        #converted into sync because channel are asynchronous
                        async_to_sync(self.channel_layer.group_add)(
                            self.room_name,
                            self.channel_name
                        )
                        async_to_sync(self.channel_layer.send)(
                            self.channel_name,
                            {
                                "type": 'chat_message',
                                "message": {"channel_id": self.channel_name}
                            }
                        )
                        self.accept()
                
                    def disconnect(self, close_code):
                        # Leave room group
                        async_to_sync(self.channel_layer.send)(
                            self.channel_name,
                            {
                                "type": 'chat_message',
                                "message": "disconnecting"
                            }
                        )
                        async_to_sync(self.channel_layer.group_discard)(
                            self.room_name,
                            self.channel_name
                        )
                        self.close()
                
                    #Receive message from WebSocket
                    def receive(self, text_data):
                        text_data_json = json.loads(text_data)
                        if "task" in text_data_json and text_data_json['task'] ==       'send_message_to_group':
                            send_message_to_group(text_data_json)
                
                
                    #Receive message from room group
                    def chat_message(self, event):
                        message = event['message']
                        #Send message to WebSocket
                        self.send_json(content={
                            'message': message
                        })
                def send_message_to_group(json_data):
                    try:
                        channel_layer = get_channel_layer()
                        g_name = str(json_data['group_name'])
                        
                   print(json_data)
                        async_to_sync(channel_layer.group_send)(
                            g_name,
                            {
                                'type': 'chat.message',
                                'message': json_data
                            }
                        )
                    except Exception as e:
                        print(str(e))
              

Note:

The async_to_sync(...) wrapper is required because SocketConsumer is a synchronous JsonWebsocketConsumer but it is calling an asynchronous channel layer method. (All channel layer methods are asynchronous.)

Group names are restricted to ASCII alphanumerics, hyphens, and periods only. Since this code constructs a group name directly from the room name, it will fail if the room name contains any characters that aren't valid in a group name so we have to replace / with _

Adding Channels Routing

Next we have to create a root routing configuration for Channels. A Channels routing configuration is an ASGI application that is similar to a Django URLconf, in that it tells Channels what code to run when an HTTP request is received by the Channels server.


           from django.urls import path,re_path
           from channels.routing import ProtocolTypeRouter, URLRouter
           from connection.consumer import SocketConsumer
           
           websockets = URLRouter([
               re_path(
                   r'^(?P<user_type>.*)$', SocketConsumer
               )
           ])
              

               from channels.routing import ProtocolTypeRouter, URLRouter
               from connection.routing import websockets
               application = ProtocolTypeRouter({
                "websocket": websockets,
               })
              

WSGI_APPLICATION = 'dashboard.wsgi.application'

ASGI_APPLICATION = 'dashboard.routing.application' # new

Adding Channels Layer

A channel layer is a kind of a communication system, which allows multiple parts of our application to exchange messages, without shuttling all the messages or events through the database.

We need a channel layer to give consumers (which we'll implement in the next step) the ability to talk to one another.

While we could use use the InMemoryChannelLayer layer since we're in development mode, we'll use a production-ready layer, RedisChannelLayer.

(env)$ docker run -p 6379:6379 -d redis:5

This command downloads the image and spins up a Redis Docker container on port 6379.

If you don't want to use Docker, feel free to download Redis directly from the official website.Alternatively you can create redis server in various cloud application like aws heroku

(env)$ pip install channels_redis


              #dashboard/settings.py
              CHANNEL_LAYERS = {
                  'default': {
                      'BACKEND': 'channels_redis.core.RedisChannelLayer',
                      'CONFIG': {
                          "hosts": [('127.0.0.1', 6379)],
                      },
                  },
              }
              

Here, we let channels_redis know where the Redis server is located.

Running Server

(env)$python manage.py runserver

Testing Connection

img

Similar Blogs To Read

img
8 Simple SEO Tips to Help you Rank your Website.

The rider can send requests anytime when he/she wants to go for a trip under a certain zone by adding their drop-off location. As a result, the app will show a flat rate of .....

img
Steps to Create and Run Asp.Net Core Application.

We need to have a strong understanding of the .NET framework, .NET Core, and one.NET before moving on to ASP.NET Core 5. So, starting with the ....

log
Solutions
Blogs
Copyright © 2024 Dhuni Software LLC. All rights reserved.