Open In App

Django Channels – Introduction and Basic Setup

Improve
Improve
Like Article
Like
Save
Share
Report

Django is a powerful Python framework for web development. It is fast, secure, and reliable. Channels allow Django projects to handle HTTP along with asynchronous protocols like WebSockets, MQTT, chatbots, and more. 

Channels:

Channels preserve the synchronous behavior of Django and add a layer of asynchronous protocols allowing users to write the views that are entirely synchronous, asynchronous, or a mixture of both. Channels basically allow the application to support “long-running connections”. It replaces Django’s default WSGI with its ASGI.

ASGI:

ASGI (Asynchronous Server Gateway Interface) provides an interface between async Python web servers and applications while it supports all the features provided by WSGI.

Consumers:

A consumer is a basic unit of Channels. It is an event-driven class that supports both async and sync applications. Consumers can run longer and hence they support web sockets that need persistent connection.

In this post, we will set up a basic example of channels. We will build a calculator app that will allow the user to send multiple expressions to the server and receive the result through a single persistent connection.

Environment Setup:

  • It is always a good idea to create a virtual environment for the python apps in order to avoid version conflicts. Run the following commands in the terminal to get started
easy-install pip
python3 -m pip install virtualenv
virtualenv venv
source venv/bin/activate
  • Now install Django and Channels:
pip install django
pip install channels
# On windows, try an unofficial wheel of 'Twisted' in case of dependency errors

LiveCalculator App:

Now start a Django project and create an app named ‘liveCalculator’

django-admin startproject sampleProject
cd sampleProject
python3 manage.py startapp liveCalculator

In sampleProject/settings.py, register channels and liveCalculator.

settings.py:

INSTALLED_APPS = [
   'channels',
   'liveCalculator',
   'django.contrib.admin',
   'django.contrib.auth',
   'django.contrib.contenttypes',
   'django.contrib.sessions',
   'django.contrib.messages',
   'django.contrib.staticfiles',
]

In sampleProject/asgi.py, add the http protocol.

asgi.py:

Python3




import os
  
import django
from channels.http import AsgiHandler
from channels.routing import ProtocolTypeRouter
  
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sampleProject.settings')
django.setup()
  
application = ProtocolTypeRouter({
  "http": AsgiHandler(),
  # Just HTTP for now. (We can add other protocols later.)
})


Now we need to register this asgi into our application. Add this line in sampleProject/settings.py :

ASGI_APPLICATION = "sampleProject.asgi.application"

Create a new folder liveCalculator/templates/liveCalculator and create a new file index.html inside it. It will be the starting page of our app. Add the following code in index.html:

index.html:

HTML




<!DOCTYPE html>
<html lang="en">
  
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Live Calculator</title>
</head>
  
<body>
    <textarea name="ta" id="results" cols="30" rows="10">
  
    </textarea><br>
    Enter the expression: <input type="text" id="exp">
    <input type="button" id="submit" value="Get Results">
    <script>
        const socket = new WebSocket('ws://localhost:8000/ws/livec/');
        socket.onmessage = (e) => {
            result = JSON.parse(e.data).result;
            document.getElementById("results").value += "Server: " + result + "\n";
        }
  
        socket.onclose = (e) => {
            console.log("Socket closed!");
        }
  
        document.querySelector('#exp').onkeyup = function (e) {
            if (e.keyCode === 13) {  // enter, return
                document.querySelector('#submit ').click();
            }
        };
  
        document.querySelector("#submit").onclick = (e) => {
            inputfield = document.querySelector("#exp")
            exp = inputfield.value
            socket.send(JSON.stringify(
                {
                    expression: exp
                }
            ))
            document.querySelector("#results").value += "You: " + exp + "\n";
            inputfield.value = "";
        }
  
    </script>
</body>
  
</html>


The above code will render a text area and an input box where the user can enter the expression. It will create a socket connection that we will make later and append the received result in the text area. When the user inputs the expression, it will send the expression through a socket connection.

Now create a view to render this page in liveCalculator/views.py :

liveCalculator/views.py:

Python3




from django.shortcuts import render
  
# Create your views here.
  
def index(request):
    return render(request, 'liveCalculator/index.html', {})


Next, we need to create a route for this view. Add a new file urls.py in liveCalculator directory and add the following code:

liveCalculator/urls.py:

Python3




from django.conf.urls import url
from . import views
  
urlpatterns = [
    url(r'^$', views.index, name="index"),
]


Register this route in sampleProject/urls.py :

sampleProject/urls.py:

Python3




from django.contrib import admin
from django.urls import path
from django.conf.urls import include, url
urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^', include('liveCalculator.urls'))
]


Now we need to create the consumer for our web socket connection. We will use the generic WebsocketConsumer class to implement its event-driven methods. Create a new file consumers.py in liveCalculator folder and add the following code:

consumers.py:

Python3




import json
from channels.generic.websocket import WebsocketConsumer
  
class Calculator(WebsocketConsumer):
    def connect(self):
        self.accept()
  
    def disconnect(self, close_code):
        self.close()   
  
    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        expression = text_data_json['expression']
        try:
            result = eval(expression)
        except Exception as e:
            result = "Invalid Expression"
        self.send(text_data=json.dumps({
            'result': result
        }))


The WebsocketConsumer class supports these user-defined methods:

  • connect(): We can write the business logic of what should happen when the client sends a connection request.
  • disconnect(): We can write the business logic of what should happen when the client sends a disconnection request.
  • receive(): We can write the business logic of what should happen when the client sends a message.

It also supports these built-in methods:

  • accept(): It will accept the incoming connection.
  • close(): It will close the current connection.
  • send(): It will send the specified message to the client.

We have simply used the above methods in our Calculator class to accept the connection, evaluate the expression when a message a received, and send it to the client.

Next, we also need to define the routing method for this consumer. Create a new file routing.py in the same folder and add the following code to it:

routing.py:

Python3




from django.urls import re_path
  
from . import consumers
  
websocket_urlpatterns = [
    re_path(r'ws/livec/$', consumers.Calculator.as_asgi()),
]


Note that we have used as_asgi() method on our Calculator class to use it for our application. This will enable the socket on ws://<IP:Port>/ws/livec. Now register routing.py into asgi.py by declaring the WebSocket protocol. 

asgi.py:

Python3




import os
  
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
import liveCalculator.routing
  
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sampleProject.settings")
  
application = ProtocolTypeRouter({
  "http": get_asgi_application(),
  "websocket": AuthMiddlewareStack(
        URLRouter(
            liveCalculator.routing.websocket_urlpatterns
        )
    ),
})


We are almost done with our first Channels application. Save all the files and run the following commands in the terminal:

python3 manage.py makemigrations
python3 manage.py migrate
python3 manage.py runserver

Now open http://localhost:8000 on your browser, and you should see the output like this:

See the log of the server. Note that we have created the connection only once, and we can send the message multiple times without creating a new connection. 



Last Updated : 13 Apr, 2021
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads