Chuyển đến nội dung chính

Bài 14: Django Channels & WebSockets

Django Channels setup, ASGI configuration. WebSocket consumers, channel layers với Redis. Real-time notifications, chat application, broadcasting patterns.

💻 Lập trình — Bài 14 Bài 14: Django Channels & WebSockets

Django: Từ Cơ bản đến Nâng cao

Phần 4: Advanced Features

xdev.asia

1. Channels Setup

pip install channels channels-redis
# settings.py
INSTALLED_APPS = [..., 'channels']
ASGI_APPLICATION = 'config.asgi.application'

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {'hosts': [('127.0.0.1', 6379)]},
    },
}

# asgi.py
import os
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from django.core.asgi import get_asgi_application

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

application = ProtocolTypeRouter({
    'http': get_asgi_application(),
    'websocket': AuthMiddlewareStack(
        URLRouter([
            path('ws/chat/<str:room_name>/', ChatConsumer.as_asgi()),
            path('ws/notifications/', NotificationConsumer.as_asgi()),
        ])
    ),
})

2. WebSocket Consumer

import json
from channels.generic.websocket import AsyncJsonWebsocketConsumer

class ChatConsumer(AsyncJsonWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group = f'chat_{self.room_name}'

        await self.channel_layer.group_add(self.room_group, self.channel_name)
        await self.accept()

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

    async def receive_json(self, content):
        message = content['message']
        user = self.scope['user']

        await self.channel_layer.group_send(
            self.room_group,
            {
                'type': 'chat_message',
                'message': message,
                'username': user.username,
            },
        )

    async def chat_message(self, event):
        await self.send_json({
            'message': event['message'],
            'username': event['username'],
        })

3. Real-time Notifications

class NotificationConsumer(AsyncJsonWebsocketConsumer):
    async def connect(self):
        user = self.scope['user']
        if user.is_anonymous:
            await self.close()
            return
        self.group_name = f'notifications_{user.id}'
        await self.channel_layer.group_add(self.group_name, self.channel_name)
        await self.accept()

    async def send_notification(self, event):
        await self.send_json(event['data'])

# Gửi notification từ anywhere
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync

def notify_user(user_id, message):
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        f'notifications_{user_id}',
        {'type': 'send_notification', 'data': {'message': message}},
    )

4. Frontend WebSocket

const ws = new WebSocket(`wss://${location.host}/ws/chat/${roomName}/`);

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  appendMessage(data.username, data.message);
};

ws.onclose = () => {
  setTimeout(() => connectWebSocket(), 3000); // Reconnect
};

function sendMessage(message) {
  ws.send(JSON.stringify({ message }));
}

Bài tiếp theo: Celery, Signals & Caching.