You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Django Channels实现实时聊天应用中消息对象的删除方法

使用Django Channels实现实时聊天消息删除

我正在使用Django Channels开发实时聊天应用,希望通过Django Channels从数据库中删除对象(具体为删除群组中的聊天消息),该如何实现?

以下是我的后端代码:

import json

from django.contrib.auth.models import User
from channels.generic.websocket import AsyncWebsocketConsumer
from asgiref.sync import sync_to_async

from .models import Room, Message

class ChatConsumer(AsyncWebsocketConsumer):

    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name

        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        await self.accept()

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

    # Receive message from WebSocket
    async def receive(self, text_data):
        data = json.loads(text_data)
        print(data)
        message = data['message']
        username = data['username']
        room = data['room']

        await self.save_message(username, room, message)

        # Send message to room group
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message,
                'username': username
            }
        )

    # Receive message from room group
    async def chat_message(self, event):
        message = event['message']
        username = event['username']

        # Send message to WebSocket
        await self.send(text_data=json.dumps({
            'message': message,
            'username': username
        }))

    @sync_to_async
    def save_message(self, username, room, message):
        user = User.objects.get(username=username)
        room = Room.objects.get(slug=room)

        Message.objects.create(user=user, room=room, content=message)

我是否需要使用JavaScript?我尝试用Ajax删除对象但没有成功。


实现方案

要实现实时消息删除,必须结合前端JavaScript——因为需要用户触发删除操作,且要通过WebSocket实时同步删除状态给群组内所有在线用户(Ajax无法主动推送更新给其他客户端)。具体步骤如下:

1. 后端ChatConsumer扩展修改

在现有代码中添加删除消息的逻辑,区分普通消息和删除请求,并向群组广播删除事件:

import json

from django.contrib.auth.models import User
from channels.generic.websocket import AsyncWebsocketConsumer
from asgiref.sync import sync_to_async

from .models import Room, Message

class ChatConsumer(AsyncWebsocketConsumer):

    # 保留原有的connect、disconnect方法
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name

        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        await self.accept()

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

    async def receive(self, text_data):
        data = json.loads(text_data)
        
        # 区分消息操作类型
        if data['action'] == 'send_message':
            message = data['message']
            username = data['username']
            room = data['room']

            # 保存消息并返回对象(用于获取ID)
            message_obj = await self.save_message(username, room, message)
            
            # 广播新消息,附带消息ID
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'chat_message',
                    'message': message,
                    'username': username,
                    'message_id': message_obj.id
                }
            )
        elif data['action'] == 'delete_message':
            message_id = data['message_id']
            username = data['username']
            
            # 执行删除并验证权限
            success = await self.delete_message(message_id, username)
            if success:
                # 广播删除事件
                await self.channel_layer.group_send(
                    self.room_group_name,
                    {
                        'type': 'chat_message_deleted',
                        'message_id': message_id
                    }
                )

    # 修改原有消息推送方法,附带消息ID
    async def chat_message(self, event):
        message = event['message']
        username = event['username']
        message_id = event['message_id']

        await self.send(text_data=json.dumps({
            'action': 'new_message',
            'message': message,
            'username': username,
            'message_id': message_id
        }))

    # 新增:处理删除事件的推送
    async def chat_message_deleted(self, event):
        message_id = event['message_id']
        
        await self.send(text_data=json.dumps({
            'action': 'message_deleted',
            'message_id': message_id
        }))

    @sync_to_async
    def save_message(self, username, room, message):
        user = User.objects.get(username=username)
        room = Room.objects.get(slug=room)
        return Message.objects.create(user=user, room=room, content=message)

    # 新增:同步删除消息的方法,包含权限验证
    @sync_to_async
    def delete_message(self, message_id, username):
        try:
            message = Message.objects.get(id=message_id)
            # 仅允许消息发布者或管理员删除(可根据需求调整)
            if message.user.username == username or message.user.is_staff:
                message.delete()
                return True
            return False
        except Message.DoesNotExist:
            return False

2. 前端JavaScript实现

前端需要完成两个核心操作:渲染消息时附带ID和删除按钮,监听WebSocket的删除事件并更新DOM:

// 初始化WebSocket连接
const roomName = "{{ room.slug }}";
const username = "{{ request.user.username }}";
const chatSocket = new WebSocket(
    'ws://' + window.location.host + '/ws/chat/' + roomName + '/'
);

// 发送普通消息
document.getElementById('chat-message-submit').addEventListener('click', function(e) {
    e.preventDefault();
    const messageInputDom = document.getElementById('chat-message-input');
    const message = messageInputDom.value;
    chatSocket.send(JSON.stringify({
        'action': 'send_message',
        'message': message,
        'username': username,
        'room': roomName
    }));
    messageInputDom.value = '';
});

// 处理WebSocket接收的消息
chatSocket.onmessage = function(e) {
    const data = JSON.parse(e.data);
    const chatLog = document.getElementById('chat-log');
    
    if (data.action === 'new_message') {
        // 渲染带ID和删除按钮的消息
        const messageElement = document.createElement('div');
        messageElement.id = `message-${data.message_id}`;
        messageElement.innerHTML = `
            <strong>${data.username}</strong>: ${data.message}
            <button onclick="deleteMessage(${data.message_id})" class="delete-btn">删除</button>
        `;
        chatLog.appendChild(messageElement);
    } else if (data.action === 'message_deleted') {
        // 移除DOM中对应的消息元素
        const messageElement = document.getElementById(`message-${data.message_id}`);
        if (messageElement) {
            messageElement.remove();
        }
    }
};

// 发送删除请求的函数
function deleteMessage(messageId) {
    chatSocket.send(JSON.stringify({
        'action': 'delete_message',
        'message_id': messageId,
        'username': username,
        'room': roomName
    }));
}

关键说明

  • 为什么不用Ajax?:Ajax只能完成单次请求响应,无法主动通知群组内其他在线用户消息已删除;WebSocket则能实时推送删除事件给所有连接客户端,保证状态同步。
  • 权限控制:后端delete_message方法中添加了权限检查,可根据业务需求调整验证逻辑。
  • 消息ID关联:所有操作都附带message_id,是关联前端DOM元素和后端数据库记录的核心标识。

内容的提问来源于stack exchange,提问作者Mehan Alavi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 05:03:36