Simple Chat App using Django Channel

Background

Python is fast becoming a popular coding language in the world, and there are many popular frameworks that build on Python. One of them is Django and it has many functionalities and supporting libraries. For this article, we like to explore one interesting method that builds on Django to handle not only HTTP but also long running connections such as WebSockets, MQTT, chatbots, etc.

To be specific, we want to utilise Django Channels to build a real-time chat application. That sounds pretty cool, isn’t it? It’s always a great experience to learn something new, so let’s begin!

First up: What is Django?

For newbies (and some oldbies), Django is a web building framework based on the Python programming language. It uses a Model-View-Template(“MVT”) architecture. Django is being popular because it is fast, secure, and a scalable high-level Python programming for web building. Learning Django is not that easy, but when you get used to it, it has many great functionalities. You could read more about Django here.

What is Django Channels?

From the Channels webpage, “Channels is a project that takes Django and extends its abilities beyond HTTP — to handle WebSockets, chat protocols, IoT protocols, and more.” Channels makes an asynchronous layer in Django so that although Django runs synchronously, Channels could handle its socket asynchronously.

Real-Time Chat Application

What is the difference between real-time chat application with the traditional chat app? Unlike most traditional chat apps, the real-time chat application does not save your chat messages in the database before passing it to other client.

That is because it uses sockets to send and receive messages between machines. The messages are exchanged through a network, either using a Transmission Control Protocol (“TCP”) or a User Datagram Protocol (“UDP”). You could save the messages if you want, but it would be separated from the socket send-receive process.

Why does the real-time chat method become popular? Firstly, it saves storage. Real-time messaging doesn’t save your messages, as the latter are just passing through the network, making your whole chat architecture storage friendly. Secondly, as its name shows, it is “real-time” and we can see that this type of messaging is faster than the normal one. The traditional messaging system saves your messages in the database, so every time you need your messages, you have to query to the database to get them. The real-time messaging only passes messages every time we want to send and receive them when they arrive on the socket, so it should be faster and more efficient.

Setting Up

Before we start to build the chat application, we need to set our environment, just like building a standard Django application. First, make a virtual environment using Python’s virtualenv and activate it. After that, you need to install some libraries (We areusing Django Rest Framework also in this example): Django 2.0, djangorestframework 3.8.2, asgi-redis==1.4.3, and channels==1.1.5 using pip install.

After installing, create a new django project,

django-admin startproject chat-project

Change directory to your project directory,

cd chat-project

Make a new app

python manage.py startapp chat-app

After that, open settings.py and add ‘rest_framework’, ‘channels’, and ‘chat-app’ to INSTALLED_APPS. You also have to add this code to settings.py in order to set up your asgi redis.

CHANNEL_LAYERS = {      
“default”: {      
“BACKEND”: “asgi_redis.RedisChannelLayer”,      
“CONFIG”: {         
“hosts”: [(redis_host, 6379)],    
},    
“ROUTING”: “routing.application”,   
},
}

Why do we need Redis? When real-time chat does not need databases like mySQL, postgreSQL, it does not mean it never save the message. It uses message broker database like Redis to save the messages in a First-in-First-Out (“FIFO”) queue before sending them.

Make It Happen

Now, it’s time for us to make the real application. To keep it simple, the example here is written without a model, so you would need to include the room name/room id on the messages. For example, we use integer as our room id, so we need to add “room_id”: <<integer>> to our JSON message. That’s how we know where we should send the message.

Move to your application chat directory, and create consumers.py. In this file, we need 6 functions:

  • def websocket_connect (message) to handle if a client socket is connecting to the server. You have to create a list of rooms here: message.channel_session[‘rooms’] = []
  • def websocket_disconnect (message) to handle if a client socket is disconnecting to the server.
  • def websocket_receive (message) to handle when the server receive messages from client socket. This function redirects the message to receive.chat using:
Channel(“receive”).send(<<message>>)

Don’t forget to add: from channels import Channel at the top of the file.

  • def join_chat(message) to handle when a new user joins a chat room. Here, you could notify to everybody in the room that a new user has joined the room. You could save the user information, for example “username”: “user-1”, in the message and send a message to the room with that information using send_chat function.
  • def leave_chat(message) to handle when a user leaves a chat room. Here, you could notify everybody that someone has left by calling send_chat function.
  • def send_chat(message) to handle when a user sends a message to a chat room. Here, you must get the Group where you would send the message. You could get it by using this code:
group = Group(“room-“ + str(message[“room_id”))

Don’t forget to add:

from channels import Group 

at the beginning of the file. After getting the group, you could send a message using that variable and send function:

group.send(<<json>>)

After finishing the consumers.py, we should make two more files called routing.py, one inside the chat-app folder and one in the project root. Here is the code for the one in the project root:

from channels import include
application = [    
# To set up the websocket routing   
include(“chat-app.routing.routing_websocket”, path=r”^/socket/”),    
# For chat join, leave, and send    
include(“chat-app.routing.routing_chat”),
]

And here is the one for inside the chat-app folder:

from channels import route

from .consumers import websocket_connect, websocket_receive, websocket _disconnect, join_chat, leave_chat, send_chat

routing_websocket = [    
  route(“websocket.connect”, websocket_connect),    
  route(“websocket.receive”, websocket_receive),    
  route(“websocket.disconnect”, websocket_disconnect),
]

routing_chat = [    
  route(“chat.receive”, join_chat, command=”^join$”),    
  route(“chat.receive”, leave_chat, command=”^leave$”),    
  route(“chat.receive”, send_chat, command=”^send$”),
]

And that’s it! You could go try making a simple HTML Javascript web application that call to the backend you have made. You could make a socket that connect to your backend like this:

var socket = new ReconnectingWebSocket(<<ws or
wss>>://<<yourhost>>/socket/);

And then you could use these function for handlers:

  • socket.onopen to handle when the socket open.
  • socket.onclose to handle when the socket is closed
  • socket.onmessage to handle when the socket receive a message
  • socket.send to send a message. It’s usually on JSON Format

This is the message example you could use:

{
“command” : <<join or leave or send>>
“room_id” : 1,
“message” : “Hi there!”
}

References

If you want to learn about making the one with a model, you could go here.

This article is written by Ranindya, developer at 9cv9 Innovation Lab. We are hiring. See more: www.9cv9.com/jobs

Was this post helpful?

Related Articles