web-dev-qa-db-fra.com

Django - Comment savoir si un utilisateur est en ligne / hors ligne en temps réel?

J'envisage d'utiliser Django-notifications et Web Sockets pour envoyer des notifications en temps réel aux applications iOS/Android et Web. Je vais donc probablement utiliser Django Channels.

Puis-je utiliser Django Channels pour suivre l'état en ligne d'un utilisateur en temps réel? Si oui, comment puis-je y parvenir sans interroger constamment le serveur?

Je recherche une meilleure pratique car je n'ai pas pu trouver de solution appropriée.

[~ # ~] mise à jour [~ # ~] :

Ce que j'ai essayé jusqu'à présent est l'approche suivante: En utilisant les canaux Django, j'ai implémenté un consommateur WebSocket qui lors de la connexion définira le statut de l'utilisateur sur 'online', Tandis que lorsque le socket sera déconnecté, le statut de l'utilisateur sera être réglé sur 'offline'. À l'origine, je voulais inclure le statut 'away', Mais mon approche ne peut pas fournir ce type d'informations. De plus, mon implémentation ne fonctionnera pas correctement lorsque l'utilisateur utilise l'application à partir de plusieurs appareils, car une connexion peut être fermée sur un appareil, mais toujours ouverte sur un autre; l'état serait défini sur 'offline' même si l'utilisateur a une autre connexion ouverte.

class MyConsumer(AsyncConsumer):

    async def websocket_connect(self, event):
        # Called when a new websocket connection is established
        print("connected", event)
        user = self.scope['user']
        self.update_user_status(user, 'online')

    async def websocket_receive(self, event):
        # Called when a message is received from the websocket
        # Method NOT used
        print("received", event)

    async def websocket_disconnect(self, event):
        # Called when a websocket is disconnected
        print("disconnected", event)
        user = self.scope['user']
        self.update_user_status(user, 'offline')

    @database_sync_to_async
    def update_user_status(self, user, status):
        """
        Updates the user `status.
        `status` can be one of the following status: 'online', 'offline' or 'away'
        """
        return UserProfile.objects.filter(pk=user.pk).update(status=status)

[~ # ~] note [~ # ~] :

Ma solution de travail actuelle utilise le cadre Django REST avec un point de terminaison API pour permettre aux applications clientes d'envoyer une demande HTTP POST avec l'état actuel. Par exemple, l'application Web suit les événements de la souris et constamment POST l'état online toutes les X secondes, lorsqu'il n'y a plus d'événements souris POST l'état away, lorsque l'onglet/la fenêtre est sur le point de se fermer, l'application envoie une demande POST avec le statut offline. CECI IS une solution qui fonctionne, selon le navigateur que j'ai des problèmes lors de l'envoi de l'état offline, mais cela fonctionne.

Ce que je recherche, c'est une meilleure solution qui n'a pas besoin d'interroger constamment le serveur.

15
Fabio

L'utilisation de WebSockets est certainement la meilleure approche.

Au lieu d'avoir un état binaire "en ligne"/"hors ligne", vous pouvez compter les connexions: Lorsqu'un nouveau WebSocket se connecte, augmentez le compteur "en ligne" de un, lorsqu'un WebSocket se déconnecte, diminuez-le. Alors, quand c'est 0, puis l'utilisateur est hors ligne sur tous les appareils.

Quelque chose comme ça

@database_sync_to_async
def update_user_incr(self, user):
    UserProfile.objects.filter(pk=user.pk).update(online=F('online') + 1)

@database_sync_to_async
def update_user_decr(self, user):
    UserProfile.objects.filter(pk=user.pk).update(online=F('online') - 1)
12
C14L

La meilleure approche consiste à utiliser les Websockets.

Mais je pense que vous devez stocker non seulement l'état, mais aussi une clé de session ou une identification de l'appareil . Si vous utilisez uniquement un compteur, vous perdez des informations précieuses, par exemple, à partir de quel appareil l'utilisateur est-il connecté à un moment spécifique. C'est la clé de certains projets. De plus, si quelque chose ne va pas (déconnexion, plantages du serveur, etc.), vous ne pourrez pas suivre le compteur lié à chaque appareil et vous devrez probablement réinitialiser le compteur à la fin.

Je vous recommande de stocker ces informations dans une autre table associée:

from Django.db import models
from Django.conf import settings


class ConnectionHistory(models.Model):
    ONLINE = 'online'
    OFFLINE = 'offline'
    STATUS = (
        (ONLINE, 'On-line'),
        (OFFLINE, 'Off-line'),
    )
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE
    )
    device_id = models.CharField(max_lenght=100)
    status = models.CharField(
        max_lenght=10, choices=STATUS,
        default=ONLINE
    )
    first_login = models.DatetimeField(auto_now_add=True)
    last_echo = models.DatetimeField(auto_now=True)

    class Meta:
        unique_together = (("user", "device_id"),)

De cette façon, vous avez un enregistrement par appareil pour suivre leur état et peut-être d'autres informations comme l'adresse IP, la géolocalisation, etc. Ensuite, vous pouvez faire quelque chose comme (en fonction de votre code):

@database_sync_to_async
def update_user_status(self, user, device_id, status):
    return ConnectionHistory.objects.get_or_create(
        user=user, device_id=device_id,
    ).update(status=status)

Comment obtenir une identification d'appareil

Il existe de nombreuses bibliothèques qui le font comme https://www.npmjs.com/package/device-uuid . Ils utilisent simplement un ensemble de paramètres de navigateur pour générer une clé de hachage. C'est mieux que d'utiliser l'ID de session seul, car il change moins souvent.

Suivi absent statut

Après chaque action, vous pouvez simplement mettre à jour last_echo. De cette façon, vous pouvez déterminer qui est connecté ou absent et de quel appareil.


Avantage: En cas de plantage, redémarrage, etc., l'état du suivi pourra être rétabli à tout moment.

6
ddiazp