[100-Day AI bootcamp] Day 2: Building a Real-Time Chatroom with Django

Posted by xkuang on January 7, 2025

Real-time communication has become an integral part of modern web applications. Developing a chat application that can handle multiple users simultaneously requires robust technology and efficient coding practices. This guide provides a step-by-step approach to building a real-time chat application using Django, leveraging AI tools to streamline development.

Understanding the Project

The goal is to create a web-based chat room application using Django that allows multiple users to communicate in real time. The application will utilize WebSockets for real-time communication, and we'll integrate AI tools to assist in coding and development.

Prerequisites

  • Basic knowledge of Python and Django framework.
  • Understanding of WebSocket protocol.
  • Familiarity with JavaScript and front-end development.

Setting Up the Development Environment

  1. Install Python and Virtual Environment

    Ensure Python is installed on your system. Create a virtual environment to isolate project dependencies.  

    python -m venv venv
    source venv/bin/activate  # On Windows use `venv\Scripts\activate`
  2. Install Django and Channels

    Django Channels are essential for handling WebSockets in Django.

    pip install django
    pip install channels

Project Structure

Create a new Django project and app:

django-admin startproject ChatRoom
cd ChatRoom
django-admin startapp chat

Configuring Django Settings

Edit ChatRoom/settings.py to include necessary configurations:

# Add 'channels' and 'chat' to INSTALLED_APPS
INSTALLED_APPS = [
    # ...
    'channels',
    'chat',
]

# Configure ASGI application
ASGI_APPLICATION = 'ChatRoom.asgi.application'

# Set up channel layers
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels.layers.InMemoryChannelLayer',
    },
}

Developing the Chat Consumer

The consumer handles WebSocket connections and communication.Create chat/consumers.py:

import json
from channels.generic.websocket import AsyncWebsocketConsumer
from datetime import datetime
import uuid

class ChatConsumer(AsyncWebsocketConsumer):
    # Class variables to store clients and messages
    clients = set()
    message_cache = []
    cache_size = 200
    client_id = 1

    async def connect(self):
        await self.accept()
        self.client_id = ChatConsumer.client_id
        ChatConsumer.client_id += 1
        self.username = f"Guest{self.client_id}"
        ChatConsumer.clients.add(self)

        # Inform other users about new connection
        await self.broadcast_message({
            'type': 'online',
            'client_id': self.client_id,
            'username': self.username,
            'datetime': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        })

        # Send message history to the new user
        for message in ChatConsumer.message_cache:
            await self.send(text_data=json.dumps(message))

    async def disconnect(self, close_code):
        ChatConsumer.clients.remove(self)
        await self.broadcast_message({
            'type': 'offline',
            'client_id': self.client_id,
            'username': self.username,
            'datetime': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        })

    async def receive(self, text_data):
        data = json.loads(text_data)
        self.username = data.get('username', self.username)

        message = {
            'id': str(uuid.uuid4()),
            'type': 'message',
            'client_id': self.client_id,
            'username': self.username,
            'body': data.get('body', ''),
            'datetime': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }

        ChatConsumer.update_cache(message)
        await self.broadcast_message(message)

    @classmethod
    def update_cache(cls, message):
        cls.message_cache.append(message)
        if len(cls.message_cache) > cls.cache_size:
            cls.message_cache = cls.message_cache[-cls.cache_size:]

    async def broadcast_message(self, message):
        for client in ChatConsumer.clients:
            await client.send(text_data=json.dumps(message))

Setting Up WebSocket Routing

Create chat/routing.py:

from django.urls import re_path
from . import consumers

websocket_urlpatterns = [
    re_path(r'ws/chat/$', consumers.ChatConsumer.as_asgi()),
]

Configure ChatRoom/asgi.py:  

import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from chat.routing import websocket_urlpatterns

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

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": AuthMiddlewareStack(
        URLRouter(
            websocket_urlpatterns
        )
    ),
})

Creating Views and Templates

Edit chat/views.py:

from django.shortcuts import render
from .consumers import ChatConsumer

def index(request):
    context = {
        'messages': ChatConsumer.message_cache,
        'clients': ChatConsumer.clients,
        'username': f"Guest{ChatConsumer.client_id}"
    }
    return render(request, 'chat/index.html', context)

Create chat/templates/chat/index.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Django Chat Room</title>
    <link rel="stylesheet" href="/static/css/chat.css" type="text/css">
    <script src="/static/js/jquery.min.js"></script>
    <script src="/static/js/chat.js"></script>
</head>
<body>
    <div id="body">
        <div id="users">
            Online Users:<br/>
            <ul id="user_list">
                {% for client in clients %}
                <li id="{{ client.client_id }}">{{ client.username }}</li>
                {% endfor %}
            </ul>
        </div>
        <div id="right">
            <div id="inbox">
                {% for message in messages %}
                {% include "chat/message.html" %}
                {% endfor %}
            </div>
            <form id="messageform">
                <table>
                    <tr>
                        <td>Username:</td>
                        <td><input name="username" id="username" style="width:100px" value="{{ username }}"></td>
                    </tr>
                    <tr>
                        <td>Message:</td>
                        <td><input name="body" id="message" style="width:500px"></td>
                    </tr>
                    <tr>
                        <td colspan="2">
                            <input type="submit" value="Send">
                        </td>
                    </tr>
                </table>
            </form>
        </div>
    </div>
</body>
</html>

Create chat/templates/chat/message.html:

<div class="message" id="m{{ message.id }}">
    {{ message.username }} says: {{ message.body }} ({{ message.datetime }})
</div>

Developing the Frontend JavaScript

Create chat/static/js/chat.js:

$(document).ready(function() {
    $("#messageform").on("submit", function(e) {
        e.preventDefault();
        newMessage($(this));
    });

    $("#messageform").on("keypress", function(e) {
        if (e.keyCode == 13) {
            newMessage($(this));
            return false;
        }
    });

    $("#message").focus();
    updater.start();
});

function addUser(id, name) {
    var ul = $('#user_list');
    var li = $('<li>').attr('id', id).text(name);
    ul.append(li);
}

function removeUser(id) {
    $('#' + id).remove();
}

function newMessage(form) {
    var message = {
        username: $("#username").val(),
        body: $("#message").val()
    };
    updater.socket.send(JSON.stringify(message));
    $("#message").val("").focus();
}

var updater = {
    socket: null,

    start: function() {
        var ws_scheme = window.location.protocol == "https:" ? "wss" : "ws";
        this.socket = new WebSocket(ws_scheme + '://' + window.location.host + "/ws/chat/");
        this.socket.onmessage = function(event) {
            var message = JSON.parse(event.data);
            updater.showMessage(message);
        };
    },

    showMessage: function(message) {
        removeUser(message.client_id);
        if (message.type !== "offline") {
            addUser(message.client_id, message.username);
        }
        if (message.type === "message" && message.body) {
            var node = $('<div>').addClass('message').attr('id', 'm' + message.id)
                .text(message.username + ' says: ' + message.body + ' (' + message.datetime + ')');
            node.hide();
            $("#inbox").append(node);
            node.slideDown();
        }
    }
};

Adding Styles to Enhance Appearance

Create chat/static/css/chat.css:

/* Styles for user list and chat messages */
#users {
    float: left;
    width: 20%;
}

#right {
    float: right;
    width: 75%;
}

#inbox {
    height: 400px;
    overflow-y: scroll;
    border: 1px solid #ccc;
    padding: 5px;
}

.message {
    margin-bottom: 5px;
}

Integrating AI Tools

To streamline development and improve code quality, consider using AI-powered code assistants like GitHub Copilot or OpenAI's ChatGPT. These tools can help generate code snippets, suggest improvements, and even identify potential bugs.

Benefits of Using AI Tools

  • Efficiency: Reduces development time by generating boilerplate code.
  • Error Reduction: Helps in spotting syntax errors and logical issues.
  • Learning Aid: Offers explanations and alternative solutions to coding problems.

Affiliate Resources for Learning and Hosting

  • Educative.io: An interactive learning platform for developers. Enhance your skills with hands-on courses tailored for all levels.

  • InterServer Hosting: Reliable hosting services for deploying your chat application. Check out their VPS options for scalable solutions.

Testing the Application

Run migrations and start the development server:

python manage.py migrate
python manage.py runserver

Open the browser and navigate to http://localhost:8000/. Enter a username and start chatting!

Key Takeaways

  • Building a real-time chat application involves backend WebSocket handling and frontend dynamic updates.
  • Django Channels simplifies the integration of WebSockets in Django projects.
  • AI tools can significantly enhance development by providing code suggestions and automation.
  • Proper project structuring and clear separation of concerns lead to maintainable and scalable applications.

References

Comments

Please log in to leave a comment.

No comments yet.