Уведомления по вебсокету в django

Я внедряю уведомления через веб-сокет в свой проект Django и испытываю проблемы с передачей пользователю количества непрочитанных уведомлений, которые у него есть на данный момент.

Моим решением этой проблемы была простая отправка всех объектов класса модели уведомления пользователю, как только он подключался к веб-сокету, в виде сообщений в самом веб-сокете, что первоначально работало, но ломалось, если было открыто более одной вкладки, так как каждая новая вкладка снова подключалась к веб-сокету, который, в свою очередь, снова отправлял сообщения веб-сокета на любую другую открытую вкладку, зарегистрированную для этого пользователя. В итоге получались дубликаты одного и того же уведомления

Мне нужен способ отображения всех уведомлений на каждой странице при ее загрузке. Желательно без отправки через контекст представления

My 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"]))

мой javascript:

Я знаю, что это беспорядок, но любые идеи, чтобы направить меня в правильном направлении, были бы очень признательны.

Я сосредоточился на самом простом возможном решении, но в моем ответе есть потенциальный подводный камень, который я описываю в конце, поэтому вам нужно будет оценить, подходит ли это для вашего случая использования.

Предполагая, что вы хотите сохранить простоту подхода "массовой рассылки", который вы имеете в настоящее время, вы можете решить эту проблему с минимальными изменениями, добавив индекс (число) к вашим уведомлениям, и отслеживая индекс последнего обработанного уведомления в каждой вкладке, реагируя только на уведомления с более высокими значениями индекса.

Просто увеличивая индекс на 1 для каждого отдельного уведомления, отправленного вашим бэкендом, ваши вкладки получают возможность отображать каждое уведомление только один раз.

Это может быть просто новая переменная index в вашем классе 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, чтобы покрыть потенциальное нарушение последовательности. На данный момент это лишь предположения, но не стесняйтесь комментировать, если вы чувствуете, что это относится к вам.

Вернуться на верх