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
相关产品推荐
相关产品推荐

