Revolutionize Your Chat App with Django Channels: Unleashing Real-Time Communication Power!

Revolutionize Your Chat App with Django Channels: Unleashing Real-Time Communication Power!

Check out my previous blog about Create A Hello World! Application Using Django Framework.

Introduction

Django Channels, an extension to Django, empowers developers to build real-time applications with ease. In this step-by-step guide, We will explore how to leverage Django Channels to create a chat application that revolutionizes communication.

Get ready to take your chat app to the next level!

Prerequisites

Before diving into this tutorial, ensure that you have the following prerequisites in place:

  • Basic understanding of Python and Django Framework.

  • Python (version 3.6 or above) and pip installed on your system.

  • Familiarity with the Django framework and its concepts.

[Note: Use Virtual Environment For Good. 👍 ]

Steps

1. Setup and Installation

To get started, follow these steps to set up the Django framework:

Install Django Framework using pip:

pip install django

Create a new Django project:

django-admin startproject myproject

Create a new Django app within your project:

cd myproject
python manage.py startapp myapp

2. Installation and Config of Channels

I. Installation of channels

Channels are available on PyPI — to install it run:

pip install -U channels["daphne"]

Check out the Best Channels Documentation.

II. Configuration of settings.py

Open myproject/settings.py and update the below:

INSTALLED_APPS = [
    # 👇 1. Add the below line
    "daphne",

    'django.contrib.admin',
    # ....
    # ...
    # ..
    # .
    'myapp',
]

TEMPLATES = [
    {
                   # 👇 2. Add the below line for templates
        'DIRS': ['templates'],
        # ....
        # ...
        # ..
        # .
    },
]

# 👇 3. Comment the below line 
# WSGI_APPLICATION = 'myproject.wsgi.application'

# 👇 4. Add the below line for asgi config
ASGI_APPLICATION = 'myproject.asgi.application'

# 👇 5. Add the below line for channel layer
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': "channels.layers.InMemoryChannelLayer"
    }
}

I. INSTALLED_APPS:

  • The INSTALLED_APPS variable in Django's settings.py file lists all the Django applications installed in your project.

  • By adding "daphne" to the INSTALLED_APPS, you are including the Daphne ASGI server as part of your project. Daphne is a high-performance ASGI server that Django Channels can use to handle WebSocket connections.

II. TEMPLATES:

  • The TEMPLATES variable in settings.py defines a list of dictionaries containing options for Django's template engine.

  • By adding DIRS to the first dictionary, you specify the directories where Django should look for template files.

  • In this case, ['templates'] indicates that Django should search for template files in a directory called "templates" within your project.

III. WSGI_APPLICATION:

  • The WSGI_APPLICATION setting specifies the WSGI application that Django uses to handle HTTP requests.

  • By commenting out the line (# WSGI_APPLICATION = 'myproject.wsgi.application'), you are disabling the default WSGI application configuration.

IV. ASGI_APPLICATION:

  • The ASGI_APPLICATION setting specifies the ASGI application that Django uses to handle WebSocket connections and other asynchronous operations.

  • By adding ASGI_APPLICATION = 'myproject.asgi.application', you are pointing Django to the ASGI application located in the "core" directory with the filename "asgi.py".

V. CHANNEL_LAYERS:

  • The CHANNEL_LAYERS setting configures the communication layer used by Django Channels for WebSocket handling.

  • By defining 'BACKEND': "channels.layers.InMemoryChannelLayer" under the 'default' key, you are using the in-memory channel layer provided by Django Channels for development purposes.

  • In production, you would typically use a more robust channel layer backend, such as Redis or RabbitMQ.*

These configuration changes are necessary for integrating Django Channels into your project and enabling real-time communication through WebSocket connections.

III. Configuration of asgi.py

Open myproject/asgi.py and update the below:

import os

# 👇 1. Update the below import lib
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from myapp.routing import websocket_urlpatterns

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

# 👇 2. Update the application var
application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": AllowedHostsOriginValidator(
            URLRouter(
                websocket_urlpatterns
            )
        ),
})

I. Importing Libraries:

  • The import os statement imports the os module, which provides a way to interact with the operating system.

  • The updated line (from django.core.asgi import get_asgi_application) imports the get_asgi_application function from the django.core.asgi module. This function returns the ASGI application object for your Django project.

II. Setting the Django Settings Module:

  • The line os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings') sets the environment variable DJANGO_SETTINGS_MODULE to specify the location of your Django project's settings.

  • In this example, it is set to 'myproject.settings', assuming your project's settings module is located in the myproject directory.

III. Defining the application Variable:

  • The application variable is a key component in the ASGI (Asynchronous Server Gateway Interface) setup.

  • The ProtocolTypeRouter class from the channels.routing module is used to define a dictionary-based protocol router that routes different protocols to appropriate applications.

In this case, the router is defined with two keys:

  • "http": The HTTP protocol is routed to the get_asgi_application() function, which returns the ASGI application object for handling HTTP requests.

  • "websocket": The WebSocket protocol is routed to the AllowedHostsOriginValidator, which is used for validating the origin of incoming WebSocket connections. It wraps the URLRouter and allows connections from allowed hosts.

  • The URLRouter is initialized with websocket_urlpatterns imported from the myapp.routing module. This variable contains the URL patterns for WebSocket connections in your application.

These changes are made in the asgi.py file of your Django project and help configure the ASGI application to handle HTTP requests and WebSocket connections using Django Channels.

Now, let’s dive into the steps required to create a chat application using Django Channels.

3. Setting up URLs and Templates

To configure the URLs and templates in your Django project, follow these steps:

I. URL Configuration:

Open the myproject/urls.py and write the below code:

from django.contrib import admin
from django.urls import path, include # 👈 1. Add this line

urlpatterns = [
    path('admin/', admin.site.urls),
    # 👇 2. Add the app url on this
    path('', include('myapp.urls'))
]

II. URL Configuration of views:

Create a new file urls.py inside the myapp folder and write the below code:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index')
]

III. Added views logic:

Open the views.py file from myapp folder and write the below code for redirecting the request to an index.html template:

from django.shortcuts import render

# Create your views here.
def index(request):
    return render(request, 'index.html')

IV. Template Configuration:

Create a folder named templates inside the myproject folder:

Now, open templates and create a new file base.html and write the below code:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Custom block for page title   -->
    <title>{% block title %}{% endblock %} | Welcome Room</title>

    <!-- Bootstrap 5 CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
  </head>
  <body>
    <!-- Custom block for content of body -->
    {% block content %}{% endblock %}

    <!-- Bootstrap 5 JS -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>

    <!-- Custom block for javascript -->
    {% block js_script %}{% endblock %}
</body>
</html>

Now, Again create a new file index.html inside the templates folder:

{% extends 'base.html' %}
{% block title %} Home {% endblock %}
{% block content %}
<div class="container mt-5">
    <h3>Welcome to Room</h3>
    <div class="row">
        <div class="col-lg-4">
            <div class="w-100">
                <div class="mb-3">
                    <label for="textMessage" class="form-label">Enter your message:</label>
                    <input type="text" class="form-control" id="textMessage">
                </div>
                <button id="addMessage" class="btn btn-primary rounded-1">Send</button>
            </div>
        </div>
        <div class="col-lg-8">
            <div id="messages" class="mt-4"></div>
        </div>
    </div>
</div>
{% endblock %}

{% block js_script %}
<script>    
    // setup chat scoket
    const chatSocket = new WebSocket(
        'ws://'
        + window.location.host
        + '/ws/chat/public_room/'
    );

    // on socket open
    chatSocket.onopen = function (e) {
        console.log('Chat socket successfully connected.');
    };

    // on socket close
    chatSocket.onclose = function (e) {
        console.log('Chat socket closed unexpectedly');
    };

    // on receiving message on group
    chatSocket.onmessage = function(e) {
        const data = JSON.parse(e.data);
        const message = data.message;

        setMessage(message);
    };

    // sending the text message to server
    document.querySelector('#addMessage').onclick = function(e) {
            const messageInputDom = document.querySelector('#textMessage');
            const message = messageInputDom.value;

            chatSocket.send(JSON.stringify({
                'message': message
            }));

            messageInputDom.value = '';
    };

    // helper func setting message
    function setMessage(message){
        var div = document.createElement('div');
        div.className = 'alert alert-success alert-dismissible fade show';
        div.setAttribute('role', 'alert');

        var message = document.createTextNode(message);
        div.appendChild(message);

        var closeButton = document.createElement('button');
        closeButton.type = 'button';
        closeButton.className = 'btn-close';
        closeButton.setAttribute('data-bs-dismiss', 'alert');
        closeButton.setAttribute('aria-label', 'Close');
        div.appendChild(closeButton);

        var container = document.getElementById('messages');  
        container.appendChild(div);
    }

</script>
{% endblock %}

The provided code represents an HTML structure for a chat room interface. Let’s go through each element:

  • <div class=”container mt-5">: This div serves as a container for the chat room interface. The container class provides a fixed-width container and mt-5 adds a margin-top to create some spacing.

  • <h3>Welcome to Room</h3>: This heading (h3) displays the welcome message or title for the chat room.

  • <div class=”row”>: This div defines a row to contain two columns.

  • <div class=”col-lg-4">: This div represents the left column in the row. It occupies 4 columns on large screens (col-lg-4 class). Adjust the class based on your layout requirements.

  • <div class=”w-100">: This div provides a full-width container within the column.

  • <div class=”mb-3">: This div adds some margin-bottom to create spacing.

  • <label for=”textMessage” class=”form-label”> Enter your message:</label>: This label element displays the text “Enter your message:” and is associated with the input field below.

  • <input type=”text” class=”form-control” id=”textMessage”>: This input element is a text input field where users can enter their messages. The form-control class is used for styling purposes, and the id attribute provides an identifier for JavaScript manipulation.

  • <button id=”addMessage” class=”btn btn-primary rounded-1">Send</button>: This button element represents a “Send” button. It has the btn and btn-primary classes for styling and the rounded-1 class to apply rounded corners. The id attribute is used for JavaScript manipulation.

  • <div class=”col-lg-8">: This div represents the right column in the row. It occupies 8 columns on large screens (col-lg-8 class). Adjust the class based on your layout requirements.

  • <div id=”messages” class=”mt-4"></div>: This div serves as a container for displaying the messages. The id attribute is used for JavaScript manipulation, and the mt-4 class adds a margin-top to create spacing.

Overall, this HTML code creates a chat room interface with a welcome message, an input field for entering messages, a “Send” button, and a container for displaying the messages. The layout is structured using Bootstrap’s grid system with columns and rows.

The provided code is a JavaScript script that handles the functionality of the chat room interface. Let’s break it down:

  • const chatSocket = new WebSocket('ws://' + window.location.host + '/ws/chat/public_room/');: This line creates a WebSocket connection by instantiating a WebSocket object. It uses the URL 'ws://' + window.location.host + '/ws/chat/public_room/' to connect to the WebSocket server. The window.location.host retrieves the current host, and /ws/chat/public_room/ represents the endpoint or URL path for the chat room WebSocket.

  • chatSocket.onopen: This event listener is triggered when the WebSocket connection is successfully opened. It logs a message to the console, indicating that the chat socket is successfully connected.

  • chatSocket.onclose: This event listener is triggered when the WebSocket connection is closed. It logs a message to the console, indicating that the chat socket was closed unexpectedly.

  • chatSocket.onmessage: This event listener is triggered when a message is received from the WebSocket server. It parses the received data into a JavaScript object using JSON.parse(), extracts the message property from the data, and calls the setMessage() function, passing the message as an argument.

  • document.querySelector('#addMessage').onclick: This event listener is triggered when the "Send" button with the id addMessage is clicked. It retrieves the value of the input field with the id textMessage, constructs a message object with the message property set to the input value, sends the message to the WebSocket server using chatSocket.send(), clears the input field, and performs any necessary updates.

  • function setMessage(message): This function is a helper function that sets the message in the chat interface. It creates a div element with the classes alert, alert-success, alert-dismissible, and fade show for styling purposes. It also appends a text node containing the message text to the div element. Additionally, it creates a close button (button element) with the classes btn-close and attributes data-bs-dismiss and aria-label for closing the alert. Finally, it retrieves the container element with the id messages and appends the created div element to it.

Overall, this JavaScript code sets up a WebSocket connection, handles events such as socket opening, socket close, and message reception, sends messages to the server, and updates the chat interface with received messages.

4. Define routing for WebSocket connections:

WebSocket connection routing in Django Channels is the process of mapping WebSocket URLs to their corresponding consumers. When a client establishes a WebSocket connection to a specific URL, the routing mechanism determines which consumer should handle the connection.

For routing of WebSocket connections is defined in a routing.py file within the project directory.

Now, Create a new file routing.py file inside the myapp folder and write the below code:

from django.urls import re_path
from . import consumers

websocket_urlpatterns = [
    re_path(r"ws/chat/public_room/", consumers.ChatConsumer.as_asgi()),
]
  • The django.urls the module is imported to access the path function, which is used for defining URL patterns.

  • The ChatConsumer class (imported from the .consumers module) is the consumer that handles WebSocket connections for the chat functionality.

  • websocket_urlpatterns is a list that contains URL patterns for WebSocket connections. Each pattern specifies a URL that begins with ws/chat/public_room.

  • The path the function is used to define the URL pattern for WebSocket connections. It takes two arguments: the URL pattern itself and the consumer that will handle the connection.

  • ChatConsumer.as_asgi() is passed as the second argument to the path function. This converts the ChatConsumer class into an ASGI application that can handle WebSocket connections.

To summarize, the code defines a URL pattern for WebSocket connections to the /ws/chat/public_room/ URL. When a client establishes a WebSocket connection to this URL, the ChatConsumer consumer is invoked to handle the connection.

5. Consumers for WebSocket connections:

In Django Channels, a consumer is a class that handles WebSocket connections and manages the communication between the server and the connected clients. Consumers are the building blocks of real-time applications built with Django Channels.

Create a new file consumers.py inside the myapp folder and write the below code:

import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer


class ChatConsumer(WebsocketConsumer):
    def connect(self):
        self.room_name = 'public_room'
        self.room_group_name = self.room_name
        # Join room group
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name, self.channel_name
        )
        self.accept()

    def disconnect(self, code):
        # Leave room group
        async_to_sync(self.channel_layer.group_discard)(
            self.room_group_name, self.channel_name
        )


    def receive(self, text_data):
        json_text = json.loads(text_data)
        message = json_text["message"]

        # Send message to room group
        async_to_sync(self.channel_layer.group_send)(
            self.room_group_name, 
            {
                "type": "chat_message", 
                "message": message
            }
        )

    def chat_message(self, event):
        message = event['message']

        # Send message to WebSocket
        self.send(text_data=json.dumps({"message": message}))

In the code, the ChatConsumer class is a subclass of WebsocketConsumer, which is a generic implementation of a consumer provided by Django Channels. Let's break down the code and understand its functionality:

I. connect():

  • This method is called when a WebSocket connection is established.

  • The room_name and room_group_name variables are initialized to identify the chat room.

  • The consumer joins the specified room_group_name by adding itself to the group using async_to_sync(self.channel_layer.group_add).

  • Finally, self.accept() is called to accept the WebSocket connection.

II. disconnect():

  • This method is called when the WebSocket connection is closed or disconnected.

  • The consumer leaves the room_group_name by removing himself from the group using async_to_sync(self.channel_layer.group_discard).

III. receive(text_data):

  • This method is called when the server receives data from the WebSocket connection.

  • The received text_data is parsed as JSON, extracting the message value.

  • The consumer sends the received message to the entire room_group_name by using async_to_sync(self.channel_layer.group_send).

IV. chat_message(event):

  • This method is responsible for handling messages sent from the room_group_name to the WebSocket connection.

  • It extracts the message from the event dictionary.

  • The consumer sends the message back to the WebSocket connection by calling self.send().

The consumer plays a crucial role in Django Channels as it manages the connection, handles incoming messages, and broadcasts messages to the relevant recipients. It provides an interface for handling events, such as connecting, disconnecting, and receiving messages, in a WebSocket-based application.

By implementing a consumer, you can define the desired behavior of your application’s WebSocket connections, enabling real-time communication and interaction between the server and connected clients.

6. Testing and Running

Now that we have set up the basic structure of our chat application using Django Channels, it’s time to test and run the app. Follow these steps:

Step 1: Open your Command-Line Interface:

Open your command-line interface and navigate to the root directory of your Django project.

To proceed, please open the terminal within the myproject folder and execute the following command:

python manage.py makemigrations
python manage.py migrate

Step 2: Start the Server:

To start the server, run the following command in your command-line interface:

python manage.py runserver

This command will launch the Django development server.

Step 3: Testing

After running the server and accessing the project interface at http://127.0.0.1:8000/ .

Open any browser and hit : http://127.0.0.1:8000/ url and you can the home page of our website looks like this:

Now, Open a new tab in another browser and hit the same url and open both tab side by side like this:

Now, with the implementation of Django Channels in our chat application, the power of real-time communication comes to life. When you send a message from one side of the chat room, it instantly appears on both sides, providing a seamless and immersive experience. This dynamic functionality fulfills our goal of fostering communication in a public room.

Imagine the possibilities! Users can engage in vibrant discussions, share ideas, and connect with others in real-time. Whether it’s a collaborative project, an online community, or a customer support channel, our chat app powered by Django Channels ensures that every message sent is received and displayed without delay, enabling smooth and interactive conversations.

Final Result,

Checkout Full Live Demo:

Now, with the implementation of Django Channels in our chat application, the power of real-time communication comes to life. When you send a message from one side of the chat room, it instantly appears on both sides, providing a seamless and immersive experience. This dynamic functionality fulfills our goal of fostering communication in a public room.

Imagine the possibilities! Users can engage in vibrant discussions, share ideas, and connect with others in real time. Whether it’s a collaborative project, an online community, or a customer support channel, our chat app powered by Django Channels ensures that every message sent is received and displayed without delay, enabling smooth and interactive conversations.

Conclusion

Congratulations! You have successfully created a chat application using Django Channels. This tutorial provided an overview of Django Channels integration into a Django project and walked you through the steps required to set up real-time communication. With Django Channels, you can expand the functionality of your chat app and bring an immersive, real-time experience to your users.

Remember to explore more features offered by Django Channels, such as handling multiple chat rooms, authentication, and message persistence, to enhance your application further.

Now, it’s time to unleash the power of Django Channels and take your chat app to new heights.

Happy Coding!

Support my passion for sharing development knowledge by making a donation to Buy Me a Coffee. Your contribution helps me create valuable content and resources. Thank you for your support!

Thank you for taking the time to read this blog about Revolutionize Your Chat App with Django Channels: Unleashing Real-Time Communication Power. I hope that you found the information presented here useful and informative.

If you have any questions or comments about the information presented in this blog, please feel free to reach out. Thank you again for reading!

Resources