首页 > 其他分享 >每天40分玩转Django:Django Channels

每天40分玩转Django:Django Channels

时间:2024-12-30 08:56:59浏览次数:5  
标签:username name self 40 Django Channels chat message room

Django Channels

一、今日学习内容概览

知识模块重点内容难度系数
WebSocket基础WebSocket协议、连接建立、消息传输★★★★☆
消息路由URL路由配置、消费者编写、消息处理★★★★☆
Channels配置项目配置、ASGI应用、Channel Layers★★★☆☆

二、WebSocket基础

1. 环境配置

首先安装必要的包:

pip install channels
pip install channels-redis
redis-server  # 确保Redis服务已启动

2. 项目配置

# settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'channels',  # 添加channels
    'chat',      # 示例应用
]

# 将ASGI应用设置为默认
ASGI_APPLICATION = 'myproject.asgi.application'

# Channel层配置
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [('127.0.0.1', 6379)],
        },
    },
}
# 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', 'myproject.settings')

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

让我们创建一个流程图来说明WebSocket连接的建立过程:
在这里插入图片描述

三、实现实时聊天室

1. 创建Consumer

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

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

        # 将channel加入群组
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )
        await self.accept()

    async def disconnect(self, close_code):
        # 将channel从群组中移除
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

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

        # 向群组发送消息
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message,
                'username': username
            }
        )

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

        # 向WebSocket发送消息
        await self.send(text_data=json.dumps({
            'message': message,
            'username': username
        }))

2. 配置路由

# chat/routing.py
from django.urls import re_path
from . import consumers

websocket_urlpatterns = [
    re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]

3. 创建视图和模板

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

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

def room(request, room_name):
    return render(request, 'chat/room.html', {
        'room_name': room_name
    })
# chat/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
    path('<str:room_name>/', views.room, name='room'),
]
<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Chat Room</title>
    <style>
        #chat-messages {
            height: 300px;
            overflow-y: scroll;
            border: 1px solid #ccc;
            padding: 10px;
            margin-bottom: 10px;
        }
    </style>
</head>
<body>
    <div id="chat-messages"></div>
    <input type="text" id="username" placeholder="Your name">
    <input type="text" id="chat-message-input" placeholder="Message">
    <button id="chat-message-submit">Send</button>

    <script>
        const roomName = {{ room_name|safe }};
        const chatSocket = new WebSocket(
            'ws://' + window.location.host + '/ws/chat/' + roomName + '/'
        );

        const messagesDiv = document.querySelector('#chat-messages');
        const messageInput = document.querySelector('#chat-message-input');
        const usernameInput = document.querySelector('#username');
        const submitButton = document.querySelector('#chat-message-submit');

        chatSocket.onmessage = function(e) {
            const data = JSON.parse(e.data);
            const messageElement = document.createElement('div');
            messageElement.textContent = `${data.username}: ${data.message}`;
            messagesDiv.appendChild(messageElement);
            messagesDiv.scrollTop = messagesDiv.scrollHeight;
        };

        chatSocket.onclose = function(e) {
            console.error('Chat socket closed unexpectedly');
        };

        messageInput.focus();
        messageInput.onkeyup = function(e) {
            if (e.keyCode === 13) {
                submitButton.click();
            }
        };

        submitButton.onclick = function(e) {
            const message = messageInput.value;
            const username = usernameInput.value || 'Anonymous';
            if (message) {
                chatSocket.send(JSON.stringify({
                    'message': message,
                    'username': username
                }));
                messageInput.value = '';
            }
        };
    </script>
</body>
</html>

4. 增加错误处理和重连机制

# chat/consumers.py
class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        try:
            self.room_name = self.scope['url_route']['kwargs']['room_name']
            self.room_group_name = f'chat_{self.room_name}'

            await self.channel_layer.group_add(
                self.room_group_name,
                self.channel_name
            )
            await self.accept()
            
            # 发送连接成功消息
            await self.send(text_data=json.dumps({
                'type': 'connection_established',
                'message': 'Connected to chat room'
            }))
            
        except Exception as e:
            await self.close(code=4000)
            
    async def receive(self, text_data):
        try:
            text_data_json = json.loads(text_data)
            message = text_data_json['message']
            username = text_data_json.get('username', 'Anonymous')
            
            # 消息验证
            if not message or len(message) > 1000:
                raise ValueError('Invalid message')
                
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'chat_message',
                    'message': message,
                    'username': username
                }
            )
            
        except json.JSONDecodeError:
            await self.send(text_data=json.dumps({
                'type': 'error',
                'message': 'Invalid JSON format'
            }))
        except ValueError as e:
            await self.send(text_data=json.dumps({
                'type': 'error',
                'message': str(e)
            }))

5. 添加用户在线状态管理

# chat/consumers.py
from channels.db import database_sync_to_async
from django.contrib.auth.models import User

class ChatConsumer(AsyncWebsocketConsumer):
    connected_users = {}
    
    @database_sync_to_async
    def get_user_info(self):
        if self.scope["user"].is_authenticated:
            return {
                'username': self.scope["user"].username,
                'id': self.scope["user"].id
            }
        return None
        
    async def connect(self):
        # ... 之前的连接代码 ...
        
        # 添加用户到在线列表
        user_info = await self.get_user_info()
        if user_info:
            self.connected_users[self.channel_name] = user_info
            
            # 广播用户上线消息
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'user_join',
                    'username': user_info['username']
                }
            )
            
    async def disconnect(self, close_code):
        # 移除用户从在线列表
        if self.channel_name in self.connected_users:
            user_info = self.connected_users.pop(self.channel_name)
            
            # 广播用户下线消息
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'user_leave',
                    'username': user_info['username']
                }
            )
            
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )
        
    async def user_join(self, event):
        await self.send(text_data=json.dumps({
            'type': 'user_join',
            'username': event['username']
        }))
        
    async def user_leave(self, event):
        await self.send(text_data=json.dumps({
            'type': 'user_leave',
            'username': event['username']
        }))

四、性能优化和最佳实践

  1. Channel Layer优化
# settings.py
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("localhost", 6379)],
            "capacity": 1500,  # 默认100
            "expiry": 10,  # 默认60
        },
    },
}
  1. 消息批处理
class ChatConsumer(AsyncWebsocketConsumer):
    message_queue = []
    
    async def receive(self, text_data):
        # ... 消息处理代码 ...
        
        self.message_queue.append({
            'type': 'chat_message',
            'message': message,
            'username': username
        })
        
        if len(self.message_queue) >= 10:
            await self.flush_message_queue()
            
    async def flush_message_queue(self):
        if self.message_queue:
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'bulk_messages',
                    'messages': self.message_queue
                }
            )
            self.message_queue = []

五、测试

# chat/tests.py
import pytest
from channels.testing import WebsocketCommunicator
from channels.routing import URLRouter
from django.urls import re_path
from .consumers import ChatConsumer

@pytest.mark.asyncio
async def test_chat_consumer():
    application = URLRouter([
        re_path(r'ws/chat/(?P<room_name>\w+)/$', ChatConsumer.as_asgi()),
    ])
    communicator = WebsocketCommunicator(
        application=application,
        path='/ws/chat/testroom/'
    )
    connected, _ = await communicator.connect()
    assert connected
    
    # 测试发送消息
    await communicator.send_json_to({
        'message': 'hello',
        'username': 'testuser'
    })
    
    response = await communicator.receive_json_from()
    assert response['message'] == 'hello'
    assert response['username'] == 'testuser'
    
    await communicator.disconnect()

六、总结与进阶建议

今天我们学习了Django Channels的核心概念和实现方法。重点包括:

  1. WebSocket基础知识
  2. Consumer的编写和配置
  3. 消息路由系统
  4. Channel Layer的使用
  5. 错误处理和重连机制
  6. 性能优化

怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!

标签:username,name,self,40,Django,Channels,chat,message,room
From: https://blog.csdn.net/weixin_40780178/article/details/144815316

相关文章

  • 408数据结构—顺序表和链表
    一、写在最前线性表是一种逻辑结构,表示元素之间一对一的相邻关系,同时还有非线性表顺序表和链表是存储结构,两者属于不同层面的概念我们可以用顺序表和链表实现线性表线性表:个数有限,相同类型,有先后次序(前驱后驱)二、顺序表定义线性表的顺序存储又称顺序表。它是用一组......
  • 408数据结构—时间复杂度的计算
    算法效率的度量是通过时间复杂度和空间复杂度来描述的。时间复杂度在统考中是一大重点,在算法设计题里通常都会要求分析时间复杂度,空间复杂度,同时还会出现考察时间复杂度的选择题,所以需要考生熟练掌握一、时间复杂度频度:一个语句的频度是指该语句在算法中被重复执行的次数......
  • 2024-2025-1 20241406刘书含 第十四周学习总结
    一、教材学习内容(一)第十四章模拟、图形学、游戏以及其他应用《计算机科学概论》第十四章主要探讨了模拟、图形学、游戏以及其他应用。以下是该章节的总结:模拟:模拟是计算的一个重要领域,它涉及为复杂系统构建计算机模型,并用模型进行实验以观察结果。模型是对真实系统的抽象,系统......
  • 2024-2025-1 20241409 《计算机基础与程序设计》第四周学习总结
    作业信息作业归属课程:https://edu.cnblogs.com/campus/besti/2024-2025-1-CFAP作业要求:https://www.cnblogs.com/rocedu/p/9577842.html#WEEK04作业目标:门电路;组合电路,逻辑电路;冯诺依曼结构;CPU,内存,IO管理;嵌入式系统,并行结构;物理安全作业正文:教材学习内容总结《计算机科学概......
  • 学期 2024-2025-1 学号 20241409《计算机基础与程序设计》第十四周学习总结
    作业信息这个作业属于哪个课程 2024-2025-1-计算机基础与程序设计这个作业要求在哪里 2024-2025-1计算机基础与程序设计第十四周作业这个作业的目标 《C语言程序设计》第十四章作业正文 本周学习内容进行了缓冲区溢出实验缓冲区溢出是指程序试图向缓冲区写入超出预分配固定......
  • 400个计算机毕业设计项目推荐(源码+文档+视频)
    计算机毕业设计案例Java毕业设计案例ASP.NET毕业设计案例PHP毕业设计案例微信小程序毕业设计案例基于Java的农产品管理系统基于ASPNETMVC的网站式音乐播放基于PHP的图书馆座位管理系统微信小程序自习室预约管理系统–2024计算机毕业设计基于Java的城市公交查询与可视化系统......
  • 每天40分玩转Django:Django类视图
    Django类视图一、知识要点概览表类别知识点掌握程度要求基础视图View、TemplateView、RedirectView深入理解通用显示视图ListView、DetailView熟练应用通用编辑视图CreateView、UpdateView、DeleteView熟练应用Mixin机制ContextMixin、LoginRequiredMixin理解原理视图配置......
  • 每天40分玩转Django:Django表单集
    Django表单集一、知识要点概览表类别知识点掌握程度要求基础概念FormSet、ModelFormSet深入理解内联表单集InlineFormSet、BaseInlineFormSet熟练应用表单集验证clean方法、验证规则熟练应用自定义配置extra、max_num、can_delete理解应用动态管理JavaScript动态添加/删除......
  • 2024-2025-1 20241403《计算机基础与程序设计》第十四周学习总结
    2024-2025-120241403《计算机基础与程序设计》第十四周学习总结作业信息这个作业属于哪个课程<班级的链接>(2024-2025-1-计算机基础与程序设计)这个作业要求在哪里<作业要求的链接>(2024-2025-1计算机基础与程序设计第十四周作业)这个作业的目标二进制文件和文本文......
  • 计算机毕业设计-基于Python+Django的信息加密解密网站系统项目开发实战(附源码+论文)
    大家好!我是程序员一帆,感谢您阅读本文,欢迎一键三连哦。......