Django's channels

This library represents django’s capacities in concurrency workflow

Nahuel Molina | Silvio
5 min readAug 27, 2021

What Python, and a great part of open source comunity have in common is how understandable they do their tools.

Django has multiple libraries that offer varied resources, as it should be. One of them is the channels library. This tool allows to establish a new connection coexisting with the HTTP one. In our case, we are going to use the web socket protocol, an asyncronous gateway that runs over TCP/IP protocol ensuring reliability and acknowledgement in the data transfer, compared to the UDP protocol.

As an overview, web socket is implemented as an specific application, creating a virtual gate in the backend waiting for data from another enabled server. They aim to exchange information decreasing the amount of authentication steps, focused on keeping alive the connection, and how fast it can turn on.

Being more technical, web sockets allows us to build out anything that requires real-time data transfer, with a basic layer of reliability.

In a frontend application is created a server that use websocket protocol that will establish a connection with the backend server. The last one receives certain data, and will acts in favor of that information, responding to the frontend socket. Javascript is perfect for implementing the client side port, injecting it to a simple HTML template. The simplest infrasctructure is a monolithic application, what means the we have templates (django templates) in the same backend project’s directory.

Channel library

This offers functions, sufficient for the server to handle the connection.First, it is mandatory to understand how Django works, in general at least. Django projects uses a core settings file that sets the entire project. It establishes global variables, specifies the development and production environment, integral and cache databases, server applications, routing, incorporation of aditional frameworks, etc. Manage.py is the kickstart that points to the setting file and incorporates all its configurations.

Let’s create a django project, the commonly examples of ws implemention is a chatrom, then get’s started:

django-admin startporject chatroom

The next step is to create an application that will specifically implement the ws connection:

python manage.py startapp chat

In the chat folder, let’s create routing.py. Using the windows’ command line..

copy con routing.py

Inside of the core folder called the same way as the project, we should modify the settings.py file, including chat in apps_installed list variable, and creating a new one, the ASGI_APPLICATION, that points torouting.py. However, do we know what is that variable? ASGI stands for Asyncronous Gateway Interface. Then we must insert our asyncronous application in there, as follows:

ASGI_APPLICATION = chatroom.routing.application

But, what is in routing.py?

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter

import chat.routing

application = ProtocolTypeRouter({
'websocket':AuthMiddlewareStack(
URLRouter(
chat.routing.websocket_urlpatterns
))
})

Look at those methods, ProtocolTypeRouter is intuitive to explain, it just signs which router to use, requiring urlspattern’s path. Basically, an asyncronous routing setting. Being a socket established, application extracts socket routes from websocket_urlpatternscontained in a second routing.py located in the chat application:

from django.urls import re_path
from . import consumers

websocket_urlpatterns = [
re_path(r'^ws/chat/(?P<room_name>\w+)/$', consmers.ChatRoomConsumer.as_asgi())
]

re_path allows us to integrate the path to the Asyncronous Socket View as an asgi with as_asgi(). In this URL we also name a variable that contains, and charges the room's name. As that code shows, the view is located in consumer.py in the same directory with the name of ChatRoomConsumer that inherits from AsyncWebsocketConsumer.

Let’s look at how the view is built:

from channels.generic.websocket import AsyncWebsocketConsumer

class ChatRoomConsumer(AsyncWebSocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name

await self.channel_layer.group_add(
self.room_group_name,
self.channel_name)

await self.accept()

async def disconnect(self, close_code):
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name)

my_counter.less()
my_counter.monitor()

async def receive(self, text_data):
text_data_json = json.loads(text_data)
result = datetime.datetime.now()

message = text_data_json['message']
username = text_data_json['username']
email = text_data_json['email']
time = str(result.hour) +':'+str(result.minute)

obj = {
'type':'chatroom_message',
'message_event':message,
'username_event':username,
'time_event':time}

await self.channel_layer.group_send(self.room_group_name, obj)

async def chatroom_message(self, event):

message = event['message_event'] #we collect the message event from the group (inside of receive function)
user_username = event['username_event'] #we collect the username too
time_message = event['time_event']

await self.send(text_data=json.dumps(people))

pass

Pay attention to the channel_layer.group_send method, this carry out the event flow. In the obj’s type field created at receive() we specify the function listener’s name, that handles messages and brings us the space to work with them. In this case, the handler is called chatroom_message and the event is the obj dictionary whose fields can be catched and saved individually.

Once the data is extracted, we can dive into details of each message that arrives, discriminating and distributing them as we prefer to build some functionality. Like youtube or twitch does at inserting commands previously designed by the administrator or his moderators. Generally those commands are differentiated from a normal message putting ! before the word(!discord).

The project structure is:

chatroom

— chatroom

— — settings

— — routing

— chat

— — routing

— — customer

The socket manager itself, which interacts directly with the client, defines then the following asyncronous methods:

  • connect(self): that asyncronous function is called each time a user connects through the socket.
  • disconnect(self): called when a user left the his session
  • receive(self): when an online client sends data, this function is responsable to take it, and act in consequence.

How exactly works django's channels

To be extremely clear, I proceed to draw the next diagram, concerning just theory concepts, putting abstract representations of the functions and classes.

I draw it

First of all, it is essential to built a socket in Javascript, and injected into the webpage’s HTML document. Each time the webpage charges, the client socket starts a connection with the django server socket through the websocket_urlpatterns path, calling async def connect(). When a specific data arrives, async def receive()handles it. Through an assigned function self.channel_layer.group_send, several messages have thrown as events, and cached by the collector chatroom_message. There can be built and executed extra functionalities proper of our application like commands (as I mentioned before), little games or surveys. Finally, and most important, a message response is sent to the group, being received by all users.

As you can see, the two applications work in parallel. One of those is an WSGI application utilizing the HTTP protocol, sending syncronously data like HTML documents or specific HTTP response, depending on the applcation itself. In the case of our monolithic, we must create templates and a descent routing system, django middlewares in form of decorators that will checks for opened sessions and redirects, ask in database, etc. The ASGI uses WebSocket protocol with one single path, focused on receiving and responding messages.

Conclusion

I think that Building a chatroom is the most explicit example to understand or visualize the event flow that occurs behind a real-time application. And besides messages, there’s other entertaining and engaging implementations that involve management of data traffic. Video streaming is possible at a little scale, using django along other tools like FFmpeg and adopting protocols like HLS. Even going beyond TCP/IP secured protocol and trying to make UDP reliable by yourself makes us internalise concepts instead of just have read it.

Game development is an alternative at converting a basic puzzle to a multiplayer puzzle. It demands a more markable focus on performance. Meanwhile messages should arrive to its detiny, in shoorter games or streaming platforms delay must be reduced.

I took this article as an introduction to websockets and real-time applications in general. I hope you like it!

--

--

Nahuel Molina | Silvio

This place is what I need for writing about programming, learning in general, and for reading people's thoughts