django中的WebSocket通知

eaf3rand  于 2022-11-11  发布在  Go
关注(0)|答案(1)|浏览(197)

我在Django项目中实现了Web Socket通知,但是在向用户传递他们当前拥有的未读通知数量时遇到了麻烦。
我对这个问题的解决方案是,一旦用户连接到Web Socket字,就将通知模型类中的所有对象作为Web套接字本身中的消息发送给用户,这最初是有效的,但是如果存在多于一个打开的标签,则中断,因为每个新的标签再次连接到Web Socket,这又将web套接字消息再次发送到登录到该用户的任何其他打开的标签。以相同通知的重复结束。
我所需要的是一种方法显示所有的通知在每一页时,它是加载。最好不发送它通过上下文的一个视图。
consumers.py:

@database_sync_to_async
def create_notification(notification):
    """
    function to create a notification.
    :param notification: notification in json format to create notification object
    """
    user = User.objects.get(username=notification["target"])
    NotificationsModel.objects.create(
        target=user,
        sender=notification["sender"],
        alertType=notification["alertType"],
        title=notification["title"],
        message=notification["message"],
    )

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()
            # Since removed the send notifications from here as they encountered the problem described above

    async def websocket_receive(self, event: dict) -> None:
        message = event["text"]  # anything sent to websocket_receive has {"type": "websocket.receive", "text": "foo"}
        type_of_handler = json.loads(message)
        type_of_handler = type_of_handler["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()

    # custom message handlers called based on "type" of message sent
    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"]))

浏览器:

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 + '/'; // ws : wss   // Websocket URL, Same on as mentioned in the routing.py
var socket = new WebSocket(webSocketEndpoint) // Creating a new Web Socket Connection
console.log('CONNECTED TO: ', webSocketEndpoint)

var counter = 0;

// Socket On receive message Functionality
socket.onmessage = function(e){
    document.getElementById("no-alerts").innerHTML = "";
    console.log('message recieved')
    const obj = JSON.parse(e.data);
    const object = JSON.parse(obj);
    console.log(object)

    var toastElList = [].slice.call(document.querySelectorAll('.toast'))
    var toastList = toastElList.map(function (toastEl) {
     return new bootstrap.Toast(toastEl)
    })

    counter = counter + 1;
    document.getElementById("badge-counter").innerHTML = counter;

    if (counter < 4) {
        // stuff to show notifications in html
    }
}

// Socket Connect Functionality
socket.onopen = function(e){
    document.getElementById("no-alerts").append("No New Alerts!");
    console.log('open', e)
}

// Socket Error Functionality
socket.onerror = function(e){
  console.log('error', e)
}

// Socket close Functionality
socket.onclose = function(e){
  console.log('closed', e)
}

有什么好主意能给我指明方向吗?

rkue9o1l

rkue9o1l1#

我在这里重点介绍了最简单的可能解决方案,但我的答案存在一个潜在的缺陷,我将在最后描述,因此您需要评估这对于您的用例是否可管理。
假设您希望保持当前使用的“批量发送”方法的简单性,您可以通过向通知添加索引(一个数字),并在每个选项卡中跟踪最后处理的通知索引,仅对具有较高索引值的通知做出React,从而通过最小的修改来解决此问题。
通过简单地将后端发送的每个不同通知的索引增加1,您的选项卡可以只显示每个通知一次。
它可以像NotificationConsumer类中的一个新索引变量(每次在send_notification中递增)和javascript中的一个全局变量(用于存储上次处理的索引)一样基本,类似于

var last_index = 0;
...

socket.onmessage = function(e){
    document.getElementById("no-alerts").innerHTML = "";
    console.log('message recieved')
    const obj = JSON.parse(e.data);
    const object = JSON.parse(obj);

    if (last_index < object.index) {
        last_index = object.index;
        //process message...
    }
...

一个潜在(但不太可能)陷阱是如果你的通知被无序发送或接收,这将导致一些通知被跳过。我不能推测这是如何通过WebSocket发生的,但如果这是一个问题,你需要在javascript端保存一个足够大的最后处理的消息数组来覆盖潜在的序列中断。这是目前的推测,但如果你觉得这对你适用,请随意发表评论。

相关问题