web-dev-qa-db-fra.com

Django: signaler quand l'utilisateur se connecte?

Dans mon application Django, je dois commencer à exécuter quelques tâches d'arrière-plan périodiques lorsqu'un utilisateur se connecte et cesser de les exécuter lorsqu'il se déconnecte. Je recherche donc un moyen élégant de

  1. être averti de la connexion/déconnexion d'un utilisateur
  2. interroger le statut de connexion de l'utilisateur

De mon point de vue, la solution idéale serait

  1. un signal envoyé par chaque Django.contrib.auth.views.login et ... views.logout
  2. une méthode Django.contrib.auth.models.User.is_logged_in(), analogue à ... User.is_active() ou ... User.is_authenticated()

Django 1.1.1 ne l’a pas et j’hésite à patcher la source et à l’ajouter (je ne sais pas comment faire cela, de toute façon).

En tant que solution temporaire, j'ai ajouté un champ booléen is_logged_in au modèle UserProfile, qui est effacé par défaut, défini la première fois que l'utilisateur accède à la page de destination (définie par LOGIN_REDIRECT_URL = '/') et interrogé dans les requêtes suivantes. Je l'ai ajouté à UserProfile. Je n'ai donc pas besoin de dériver et de personnaliser le modèle utilisateur intégré uniquement à cette fin.

Je n'aime pas cette solution. Si l'utilisateur clique explicitement sur le bouton de déconnexion, je peux effacer l'indicateur, mais la plupart du temps, il suffit de laisser la page ou de fermer le navigateur. Dégager le drapeau dans ces cas ne me semble pas simple. En plus (c’est plutôt une recherche de clarté dans la clarté du modèle de données), is_logged_in n’appartient pas à UserProfile, mais au modèle User.

Quelqu'un peut-il penser aux approches alternatives?

70
ssc

Vous pouvez utiliser un signal comme celui-ci (je mets le mien dans models.py)

from Django.contrib.auth.signals import user_logged_in


def do_stuff(sender, user, request, **kwargs):
    whatever...

user_logged_in.connect(do_stuff)

Voir la documentation Django: https://docs.djangoproject.com/en/dev/ref/contrib/auth/#module-Django.contrib.auth.signals et ici http: //docs.djangoproject. com/fr/dev/topics/signaux/

133
PhoebeB

Une option pourrait consister à envelopper les vues de connexion/déconnexion de Django avec les vôtres. Par exemple:

from Django.contrib.auth.views import login, logout

def my_login(request, *args, **kwargs):
    response = login(request, *args, **kwargs)
    #fire a signal, or equivalent
    return response

def my_logout(request, *args, **kwargs):
    #fire a signal, or equivalent
    return logout(request, *args, **kwargs)

Vous utilisez ensuite ces vues dans votre code plutôt que celles de Django et le tour est joué.

En ce qui concerne l’interrogation du statut de connexion, c’est assez simple si vous avez accès à l’objet request; il suffit de vérifier l'attribut utilisateur de la requête pour savoir s'il s'agit d'un utilisateur enregistré ou anonyme et le bingo. Pour citer la documentation Django :

if request.user.is_authenticated():
    # Do something for logged-in users.
else:
    # Do something for anonymous users.

Si vous n'avez pas accès à l'objet de requête, il sera difficile de déterminer si l'utilisateur actuel est connecté.

Modifier:

Malheureusement, vous ne pourrez jamais obtenir la fonctionnalité User.is_logged_in() - c'est une limitation du protocole HTTP. Cependant, si vous faites quelques hypothèses, vous pourrez peut-être vous rapprocher de ce que vous voulez.

Premièrement, pourquoi ne pouvez-vous pas obtenir cette fonctionnalité? Eh bien, vous ne pouvez pas faire la différence entre une personne qui ferme le navigateur ou une personne qui passe du temps sur une page avant d’en récupérer une nouvelle. Il n'y a aucun moyen de savoir via HTTP si une personne quitte réellement le site ou ferme le navigateur.

Donc, vous avez deux options qui ne sont pas parfaites:

  1. Utilisez l'événement unload de Javascript pour intercepter un utilisateur qui quitte une page. Cependant, vous devrez écrire une logique minutieuse pour vous assurer de ne pas déconnecter un utilisateur qui navigue toujours sur le site votre _.
  2. Activez le signal de déconnexion chaque fois qu'un utilisateur se connecte, juste pour être sûr. Créez également un travail cron qui s'exécute assez souvent pour vider les sessions expirées. Lorsqu'une session expirée est supprimée, vérifiez que l'utilisateur de la session (s'il n'est pas anonyme) n'a plus de session active, auquel cas vous déclenchez le signal de déconnexion.

Ces solutions sont désordonnées et pas idéales, mais ce sont les meilleures que vous puissiez faire, malheureusement.

13
ShZ

En plus de la réponse de @PhoebeB: .__, vous pouvez également utiliser le décorateur @receiver comme ceci:

from Django.contrib.auth.signals import user_logged_in
from Django.dispatch import receiver

@receiver(user_logged_in)
def post_login(sender, user, request, **kwargs):
    ...do your stuff..`

Et si vous le mettez dans signals.py dans votre répertoire d'application, ajoutez ceci à app.py:

def ready(self):
    import app_name.signals`
7
bershika

Déduire la déconnexion, par opposition à les faire explicitement cliquer sur un bouton (ce que personne ne fait), signifie choisir une durée d'inactivité équivalente à "déconnectée". phpMyAdmin utilise une valeur par défaut de 15 minutes; certains sites bancaires utilisent aussi peu que 5 minutes.

Le moyen le plus simple de mettre cela en œuvre serait de changer la durée de vie des cookies. Vous pouvez le faire pour l’ensemble du site en spécifiant settings.SESSION_COOKIE_AGE . Vous pouvez également le modifier utilisateur par utilisateur (en fonction de critères arbitraires) en utilisant HttpResponse.setcookie() . Vous pouvez centraliser ce code en créant votre propre version de render_to_response() et en la faisant définir la durée de vie de chaque réponse.

1
Peter Rowell

Le seul moyen fiable (qui détecte également lorsque l'utilisateur a fermé le navigateur) est de mettre à jour un champ last_request chaque fois que l'utilisateur charge une page.

Vous pouvez également avoir une demande périodique AJAX qui envoie une requête ping au serveur toutes les x minutes si l'utilisateur a une page ouverte.

Ensuite, créez un travail en arrière-plan avec une liste d'utilisateurs récents, créez-leur des travaux et effacez les travaux des utilisateurs ne figurant pas dans cette liste.

1
Joel L

Idée approximative - vous pouvez utiliser un middleware pour cela. Ce middleware peut traiter les demandes et déclencher un signal lorsqu'une URL pertinente est demandée. Il pourrait également traiter les réponses et les signaux d’incendie lorsqu’une action est effectivement menée à bien.

0
Tomasz Zielinski

une solution rapide pour cela serait, dans le _ _ init _ _.py de votre application, placez le code suivant:

from Django.contrib.auth.signals import user_logged_in
from Django.dispatch import receiver


@receiver(user_logged_in)
def on_login(sender, user, request, **kwargs):
    print('User just logged in....')
0
Rui Lima