Nouveau : Datasets open source gratuits disponibles !Decouvrir →
🐍
Avance 25 min Django

WebSockets avec Django

Pourquoi WebSockets avec Django ?

Dans un monde où les applications web évoluent constamment vers une expérience utilisateur plus dynamique et réactive, les WebSockets sont devenus un atout majeur. En effet, ils permettent aux serveurs de transmettre des données en temps réel à leurs clients, sans la nécessité d'un rechargement complet de la page. C'est particulièrement utile pour des applications comme les chatbots, les jeux interactifs, les plateformes de collaboration en temps réel, ou tout autre système où le flux continu d'informations est essentiel.

Un cas concret de l'utilisation de WebSockets avec Django est une application de messagerie instantanée. Dans ce cas, chaque fois qu'un message est envoyé, il doit être immédiatement visible pour tous les utilisateurs connectés, sans avoir besoin d'actualiser la page manuellement.

Prerequis

  • Connaissance avancée de Python et Django
  • Connaissances en JavaScript et HTML/CSS (pour le frontend)
  • Installation de Docker (facultatif mais recommandé pour une meilleure isolation des environnements)

Concepts fondamentaux

1. WebSockets vs HTTP

Les WebSockets sont différents des requêtes HTTP traditionnelles :

  • HTTP : Basé sur un modèle request-response, chaque requête nécessite une réponse avant la prochaine.
  • WebSockets : Établit une connexion persistante entre le client et le serveur. Une fois établie, les deux parties peuvent envoyer et recevoir des données à tout moment.

2. Channels

Django Channels est un package tiers qui permet d'ajouter la prise en charge de WebSockets à Django. Il étend les capacités du framework pour gérer le transport de messages dans une application concurrente.

3. Consumers

Les consumers sont des classes ou fonctions qui traitent les messages entrants et sortants. Ils peuvent être synchrones ou asynchrones, ce qui permet une grande flexibilité.

4. Channels Layer

Le channels layer est un mécanisme de communication entre les workers et les consumers. Il stocke les données en attente pour chaque consumer.

Mise en pratique : projet fil rouge

Nous allons construire une application simple d'un chat interactif en utilisant Django et Channels. L'application permettra aux utilisateurs de se connecter, d'échanger des messages en temps réel, et de voir les messages apparaître instantanément pour tous.

Étape 1 : Initialisation du projet

## Créer un nouveau projet Django
django-admin startproject chat_app

## Accéder au dossier du projet
cd chat_app

## Créer une nouvelle application Django
python manage.py startapp chat

Ajoutez l'application chat à la liste des applications dans settings.py :

INSTALLED_APPS = [
    ...
    'channels',
    'chat',
]

Étape 2 : Configuration de Channels

Créez un fichier asgi.py à la racine du projet pour configurer ASGI (Asynchronous Server Gateway Interface) :

## asgi.py
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from django.urls import path
from chat import consumers

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

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": URLRouter([
        path("ws/chat/", consumers.ChatConsumer.as_asgi()),
    ]),
})

Étape 3 : Création du consumer

Créez un fichier consumers.py dans l'application chat :

## chat/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        await self.accept()

    async def disconnect(self, close_code):
        pass

    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        # Broadcast the message to all connected clients
        await self.channel_layer.group_send(
            'chat_group',
            {
                'type': 'chat_message',
                'message': message
            }
        )

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

        # Send the received message back to the client
        await self.send(text_data=json.dumps({
            'message': message
        }))

Étape 4 : Configuration des groupes

Ajoutez le code suivant à asgi.py pour gérer les groupes de clients :

## asgi.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter, ChannelNameRouter
from django.core.asgi import get_asgi_application
from chat.consumers import ChatConsumer

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": AuthMiddlewareStack(
        URLRouter([
            path("ws/chat/", ChatConsumer.as_asgi()),
        ])
    ),
})

Étape 5 : Configuration de Channels dans settings.py

Ajoutez les configurations nécessaires pour Channels :

## settings.py
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels.layers.InMemoryChannelLayer',
    },
}

Étape 6 : Création du template HTML

Créez un fichier index.html dans le dossier templates/chat/ :

<!-- templates/chat/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chat App</title>
    <script type="text/javascript">
        const socket = new WebSocket(`ws://${window.location.host}/ws/chat/`);

        socket.onmessage = function(event) {
            const data = JSON.parse(event.data);
            const messageElement = document.createElement('div');
            messageElement.textContent = data.message;
            document.getElementById('messages').appendChild(messageElement);
        };

        function sendMessage() {
            const input = document.getElementById('message-input');
            socket.send(JSON.stringify({
                'message': input.value
            }));
            input.value = '';
        }
    </script>
</head>
<body>
    <h1>Chat App</h1>
    <div id="messages"></div>
    <input type="text" id="message-input">
    <button onclick="sendMessage()">Send</button>
</body>
</html>

Étape 7 : Création de la route pour le template

Ajoutez une URL pour accéder à la page du chat :

## chat/urls.py
from django.urls import path
from .views import index

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

Ajoutez cette URL au fichier urls.py principal du projet :

## chat_app/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('chat.urls')),
]

Étape 8 : Création de la vue pour le template

Créez un fichier views.py dans l'application chat :

## chat/views.py
from django.shortcuts import render

def index(request):
    return render(request, 'chat/index.html')

Erreurs frequentes et debugging

1. Erreur : TypeError: 'NoneType' object is not callable

Code incorrect :

## consumers.py
class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        await self.accept()

    async def disconnect(self, close_code):
        pass

    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        # Broadcast the message to all connected clients
        await self.channel_layer.group_send(
            'chat_group',
            {
                'type': 'chat_message',
                'message': message
            }
        )

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

        # Send the received message back to the client
        await self.send(text_data=json.dumps({
            'message': message
        }))

Code correct :

## consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        await self.accept()

    async def disconnect(self, close_code):
        pass

    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        # Broadcast the message to all connected clients
        await self.channel_layer.group_send(
            'chat_group',
            {
                'type': 'chat_message',
                'message': message
            }
        )

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

        # Send the received message back to the client
        await self.send(text_data=json.dumps({
            'message': message
        }))

2. Erreur : AttributeError: module 'channels.layers' has no attribute 'InMemoryChannelLayer'

Code incorrect :

## settings.py
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels.layers.InMemoryChannelLayer',
    },
}

Code correct :

## settings.py
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels.layers.InMemoryChannelLayer',
    },
}

3. Erreur : AttributeError: module 'channels.generic.websocket' has no attribute 'AsyncWebsocketConsumer'

Code incorrect :

## consumers.py
from channels.generic.websocket import AsyncWebsocketConsumer

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        await self.accept()

    async def disconnect(self, close_code):
        pass

    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        # Broadcast the message to all connected clients
        await self.channel_layer.group_send(
            'chat_group',
            {
                'type': 'chat_message',
                'message': message
            }
        )

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

        # Send the received message back to the client
        await self.send(text_data=json.dumps({
            'message': message
        }))

Code correct :

## consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        await self.accept()

    async def disconnect(self, close_code):
        pass

    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        # Broadcast the message to all connected clients
        await self.channel_layer.group_send(
            'chat_group',
            {
                'type': 'chat_message',
                'message': message
            }
        )

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

        # Send the received message back to the client
        await self.send(text_data=json.dumps({
            'message': message
        }))

Pour aller plus loin

  1. Authentification JWT avec Channels : Utilisez un token JWT pour l'authentification des utilisateurs avant de leur permettre d'échanger des messages.
  2. Gestion des groupes avancée : Implémentez la gestion des groupes pour différer les messages aux utilisateurs spécifiques ou aux groupes de utilisateurs.
  3. Personnalisation du consumer : Créez des consumers plus complexes pour gérer différents types de messages et des événements.

Défi pratique : Ajoutez une fonctionnalité permettant à chaque utilisateur de créer un groupe privé avec d'autres utilisateurs.

Besoin d'aide sur Django ?

Besoin d'aide sur un projet technique ? Decrivez-le pour des conseils personnalises.

Recevoir des conseils

Questions frequentes

Quelles sont les principales avantages de l'utilisation de WebSockets avec Django?
L'utilisation de WebSockets avec Django offre plusieurs avantages, notamment une communication en temps réel entre le serveur et le client, la possibilité d'éviter la surcharge réseau due aux requêtes HTTP traditionnelles et un amélioration significative des performances pour les applications nécessitant une interaction fluide et instantanée.
Comment installer Django Channels pour utiliser WebSockets?
Pour installer Django Channels, utilisez la commande pip : `pip install channels`. Ensuite, ajoutez 'channels' à l'attribut `INSTALLED_APPS` de votre fichier settings.py et configurez les routes WebSocket dans le fichier asgi.py de votre projet.
Quelles sont les principales considérations pour sécuriser une application WebSockets utilisant Django?
Il est crucial de sécuriser les connexions WebSockets, surtout lorsqu'elles transmettent des informations sensibles. Utilisez HTTPS pour encrypter la communication, vérifiez l'authentification et l'autorisation des utilisateurs, et utilisez des protocoles sécurisés comme WebSocket Secure (WSS). Assurez-vous également de mettre en œuvre des mesures de protection contre les attaques telles que le Cross-Site Scripting (XSS) et la Cross-Site Request Forgery (CSRF).

Pages liees

Chaque semaine, le meilleur de la tech francaise

Tendances, salaires, outils et opportunites — directement dans votre boite mail.

Gratuit. Desabonnement en un clic. Pas de spam.