Django Channels 3 Basics — Building a Simple App

Video Tutorial

Code on Github

In this tutorial we are going to build probably the most basic Django Channels app that we can. I really want to get into more Django Channels tutorials, but it can be a little complicated so for this first one we will just build a simple example. Hopefully this will help with understanding the basic pieces needed to run a Channels app. This won’t be a beginner Django video, you should already know the basics of Django before starting this. For this example, we will build an app that counts up to 10 and starts over. This will run and update without any page reloads. Let’s get started building this example.

Install Django & Django Channels

First things first, we will install Django and Django Channels and create an example app. Create a new Django project and create an app called ‘counter’ we can then install Django Channels using this:

pip install -U channels

Make sure to add the counter app and the channels app to the settings.py file.

Set Up ASGI

We first need to update the asgi.py file. This will allow us to run async code. Channels will use this to run. Channels has a built in class called ProtocolTypeRouter that will allow us to use different protocols for our requests. We will use http like we usually do to run the views and show the templates but we will use the ws protocol to send the updated data to the frontend to update our counter. Right now we will just set up http using the ProtocolTypeRouter. We will come back and add more later. This is how the updated file should look:

import osfrom django.core.asgi import get_asgi_applicationfrom channels.routing import ProtocolTypeRouteros.environ.setdefault('DJANGO_SETTINGS_MODULE', 'basic.settings')application = ProtocolTypeRouter({  'http': get_asgi_application(),})

Set Up Django View, URL, and Template For the Index Page

Now let’s set up a basic index page using regular Django. We will use the generic View class as well.

counter/views.py:

from django.shortcuts import renderfrom django.views import Viewclass Index(View):  def get(self, request):  context = {    'count': 'Hello World'  }  return render(request, 'counter/index.html', context)

counter/urls.py:

from django.urls import pathfrom .views import Indexurlpatterns = [  path('', Index.as_view())]

counter/templates/counter/index.html:

<!DOCTYPE html>  <html lang="en">    <head>      <meta charset="UTF-8">      <meta http-equiv="X-UA-Compatible" content="IE=edge">      <meta name="viewport" content="width=device-width, initial-scale=1.0">      <title>Real Time Django Channels App</title>    </head>    <body>      <h1 id="number">{{ count }}</h1>    </body>  </html>

All we are doing is showing some text on the template, right now it should say ‘Hello World’ we will replace this text now with a counter that counts from 1 to 10.

Set Up Routing and The Consumer

In Django Channels we need a routing file which will be pretty much the same as a urls.py file except instead of our http:// urls we will need some urls for our ws:// protocol paths. This will just contain one path that will handle the logic for the counter. Let’s set that file up first.

counter/routing.py:

from django.urls import pathfrom .consumers import WSConsumerws_urlpatterns = [  path('ws/counter/', WSConsumer.as_asgi())]

This should look very similar to previous urls. We will need to create a new class called WSConsumer which is the class this path is using. A consumer is pretty much the same thing as a View in Django it is just called an Consumer in Django Channels. Let’s create that file and the consumer now.

counter/consumers.py:

from channels.generic.websocket import WebsocketConsumerimport jsonfrom time import sleepclass WSConsumer(WebsocketConsumer):  def connect(self):    self.accept()
count = 0
for i in range(1000): if count < 10: count += 1 self.send(json.dumps({'message': count})) sleep(1) else: count = 1 self.send(json.dumps({'message': count})) sleep(1)

All this consumer is doing is accepting the connection, setting count to 0 and then we are creating a loop to send the numbers over and over again, this is just an example so this should be ok for this. We have an if else statement to check if the number is less than 10 if it is we will increment it, if not we will set the number back to 1. We will then send JSON to the frontend. Which will contain ‘message’ and we will set its value to the count variable. We will sleep for one second in this example to slow down the numbers when we update it. And that is all we need for the routing and the consumer. Now let’s update our asgi.py file to handle the web socket protocol requests.

Update ASGI With The New Web Socket

import osfrom django.core.asgi import get_asgi_applicationfrom channels.routing import ProtocolTypeRouter, URLRouterfrom channels.auth import AuthMiddlewareStackfrom counter.routing import ws_urlpatternsos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'basic.settings')application = ProtocolTypeRouter({  'http': get_asgi_application(),  'websocket': AuthMiddlewareStack(URLRouter(ws_urlpatterns))})

We wil use a couple more classes, AuthMiddlewareStack and URLRouter to authenticate the request user and the URLRouter will use our routing urls to get the correct consumer based on the request it is receiving.

Add Javascript to Update the Template

Now we need to actually update the frontend. We will write some Javascript to do this. We will instantiate a new WebSocket and we will send a request to our counting url path. We can then update the DOM with the response.

Here is the updated index.html file:

<!DOCTYPE html><html lang="en">  <head>    <meta charset="UTF-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title>Real Time Django Channels App</title>  </head>  <body>    <h1 id="number">{{ count }}</h1>    <script>      var socket = new WebSocket('ws://localhost:8000/ws/counter/');      socket.onmessage = function(event) {        var data = JSON.parse(event.data);        console.log(data);        document.querySelector('#number').innerText = data.message;      }    </script>
</body>
</html>

We will use the onmessage property to update the DOM once the socket receives a message. And then we can grab the message key out of that, which is what we set in our consumer to hold the count variable. We can then grab the h1 with the id of number and update to the count variable in data.message.

And this is it, now the page should update every second and count up to 10 and start over. This was obviously just a very basic example but hopefully that will help in getting a basic understanding of the pieces to a Django Channels app. Now we can go into more advanced projects using this in the future.