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

Django中WebSocket通知未读数量传递及多标签重复问题求助

解决Django WebSocket通知的两个核心问题

问题梳理

  • 无法传递用户当前的未读通知数量
  • 多标签页连接WebSocket时,重复发送历史通知导致页面重复显示;需要页面加载时展示所有通知且无需通过视图上下文传递数据

针对性解决方向

1. 初始化WebSocket时仅向当前连接发送历史通知与未读数量

修改consumers.py,在用户连接时异步查询未读数据,只发给当前连接的Channel,不广播到用户组,避免多标签页互相干扰:

import json
from channels.db import database_sync_to_async
from .models import NotificationsModel, User
from channels.generic.websocket import AsyncWebsocketConsumer

@database_sync_to_async
def get_user_unread_notifications(user):
    # 假设模型有is_read字段,没有的话先新增该字段标记已读状态
    return list(NotificationsModel.objects.filter(target=user, is_read=False).values())

@database_sync_to_async
def get_unread_count(user):
    return NotificationsModel.objects.filter(target=user, is_read=False).count()

class NotificationConsumer(AsyncWebsocketConsumer):
    async def websocket_connect(self, event: dict) -> None:
        if self.scope["user"].is_anonymous:
            await self.close()
        else:
            self.group_code = self.scope["url_route"]["kwargs"]["group_code"]
            await self.channel_layer.group_add(self.group_code, self.channel_name)
            await self.accept()
            
            # 仅向当前连接发送初始化数据
            user = self.scope["user"]
            unread_count = await get_unread_count(user)
            unread_notifications = await get_user_unread_notifications(user)
            
            await self.send(text_data=json.dumps({
                "type": "init",
                "unread_count": unread_count,
                "notifications": unread_notifications
            }))

    # 保留原有其他方法不变
    async def websocket_receive(self, event: dict) -> None:
        message = event["text"]
        type_of_handler = json.loads(message)["type"]
        await self.channel_layer.group_send(self.group_code, {"type": type_of_handler, "message": message})

    async def websocket_disconnect(self, event: dict) -> None:
        await self.channel_layer.group_discard(self.group_code, self.channel_name)
        await self.close()

    async def send_notification(self, event: dict) -> None:
        await self.send(json.dumps({"type": "websocket.send", "message": event}))

    async def notify_and_create(self, event: dict) -> None:
        message = json.loads(event["message"])
        await create_notification(message["notification"])
        notification = json.dumps(message["notification"])
        await self.channel_layer.group_send(
            str(message["notification"]["target_id"]), {"type": "notification", "message": notification}
        )

    async def notification(self, event: dict) -> None:
        await self.send(text_data=json.dumps(event["message"]))

2. 前端区分初始化消息与实时通知

修改JS代码,根据消息类型分别处理历史通知渲染和新通知提醒,避免计数器混乱:

try{
    var groupCode = document.getElementById("user");
    var user = groupCode.getAttribute('user')
}catch(err){
    throw Error('notifications not active (do not worry about this error)')
}

var loc = window.location;
var wsStart = "ws://";
if (loc.protocol == "https:"){
  wsStart = "wss://";
}
var webSocketEndpoint =  wsStart + loc.host + '/ws/notifications/' + user + '/';
var socket = new WebSocket(webSocketEndpoint)
console.log('CONNECTED TO: ', webSocketEndpoint)

var counter = 0;

// Socket On receive message Functionality
socket.onmessage = function(e){
    document.getElementById("no-alerts").innerHTML = "";
    const data = JSON.parse(e.data);
    
    if (data.type === "init") {
        // 初始化:设置未读数量,渲染历史通知
        counter = data.unread_count;
        document.getElementById("badge-counter").innerHTML = counter;
        
        // 遍历历史通知并渲染到页面
        data.notifications.forEach(notification => {
            // 这里替换成你的通知DOM渲染逻辑
            console.log("历史通知:", notification);
        });
        
        if (counter === 0) {
            document.getElementById("no-alerts").append("No New Alerts!");
        }
    } else if (data.type === "notification") {
        // 处理实时新通知
        const object = JSON.parse(data.message);
        console.log("新通知:", object);
        
        counter += 1;
        document.getElementById("badge-counter").innerHTML = counter;
        
        if (counter < 4) {
            // 新通知的弹窗/列表插入逻辑
        }
    }
}

// 保留原有其他WebSocket事件处理不变
socket.onopen = function(e){
    console.log('open', e)
}

socket.onerror = function(e){
  console.log('error', e)
}

socket.onclose = function(e){
  console.log('closed', e)
}

3. 多标签页重复问题的核心逻辑修正

之前的问题是连接时把历史通知广播到用户组,导致所有已打开的标签页重复接收。现在改为仅向当前连接发送初始化数据,用户组只用于推送新通知——这样新标签页加载时只会给自己发历史数据,不会干扰其他标签页,而新通知会同步推送给所有用户标签页(符合正常使用场景)。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 21:27:28