84 votes

Django : signal lorsque l'utilisateur se connecte ?

Dans mon application Django, je dois commencer à exécuter quelques tâches périodiques en arrière-plan lorsqu'un utilisateur se connecte et les arrêter lorsque l'utilisateur se déconnecte.

  1. être informé de la connexion ou de la déconnexion d'un utilisateur
  2. demander 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() analogues à ... User.is_active() ou ... User.is_authenticated()

Django 1.1.1 n'en dispose pas et je suis réticent à patcher le code source pour l'ajouter (je ne suis pas sûr de savoir comment le faire, de toute façon).

Comme solution temporaire, j'ai ajouté un is_logged_in dans le modèle UserProfile, qui est désactivé par défaut, est activé la première fois que l'utilisateur accède à la page de renvoi (définie par le paramètre LOGIN_REDIRECT_URL = '/' ) et est interrogé dans les requêtes suivantes. Je l'ai ajouté à UserProfile, pour ne pas avoir à dériver du modèle User intégré et à le personnaliser dans ce seul but.

Je n'aime pas cette solution. Si l'utilisateur clique explicitement sur le bouton de déconnexion, je peux effacer le drapeau, mais la plupart du temps, les utilisateurs quittent simplement la page ou ferment le navigateur ; effacer le drapeau dans ces cas ne me semble pas simple. Par ailleurs, il s'agit plutôt de pinailler sur la clarté du modèle de données, is_logged_in n'a pas sa place dans le UserProfile, mais dans le modèle User.

Quelqu'un peut-il penser à d'autres approches ?

4 votes

Veuillez envisager de sélectionner une nouvelle réponse. La réponse actuellement acceptée est un très mauvais choix à la lumière du signal ajouté en 1.3.

1 votes

Vous avez raison ; j'ai changé la réponse acceptée.

157voto

PhoebeB Points 2862

Vous pouvez utiliser un signal comme ceci (j'ai mis 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 de django : https://docs.djangoproject.com/en/dev/ref/contrib/auth/#module-django.contrib.auth.signals et ici http://docs.djangoproject.com/en/dev/topics/signals/

8 votes

Maintenant que Django 1.3 offre ces signaux, c'est une beaucoup meilleure solution que d'envelopper l'appel à la connexion/déconnexion. Cela signifie également que si vous mettez en place de nouvelles méthodes de connexion - par exemple, des connexions Facebook/Twitter/OpenID - elles fonctionneront toujours.

9 votes

Plutôt que de mettre cela dans models.py Je suggère plutôt de placer le code dans signals.py et de l'importer automatiquement dans l'arborescence des modules. __init__.py fichier.

0 votes

Comment utiliser ce signal pour exécuter le javascript à la connexion et à la déconnexion ?

13voto

ShZ Points 3431

Une option pourrait être d'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 dans celui de Django, et voilà.

En ce qui concerne l'interrogation du statut de connexion, c'est assez simple si vous avez accès à l'objet de requête ; il suffit de vérifier l'attribut utilisateur de la requête pour voir s'il s'agit d'un utilisateur enregistré ou d'un utilisateur anonyme, et bingo. Pour citer le Documentation sur 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é.

Edit :

Malheureusement, vous ne serez jamais en mesure d'obtenir User.is_logged_in() Il s'agit d'une limitation du protocole HTTP. Cependant, si vous faites quelques hypothèses, vous pourrez peut-être vous rapprocher de ce que vous voulez.

D'abord, pourquoi ne pouvez-vous pas obtenir cette fonctionnalité ? Eh bien, il est impossible de faire la différence entre une personne qui ferme son navigateur et une personne qui reste un certain temps sur une page avant d'en récupérer une nouvelle. Il n'y a aucun moyen de savoir, via HTTP, quand quelqu'un quitte effectivement le site ou ferme le navigateur.

Vous avez donc deux options ici qui ne sont pas parfaites :

  1. Utilisez la fonction unload pour savoir quand un utilisateur quitte une page. Vous devrez écrire une logique prudente pour vous assurer que vous ne déconnectez pas un utilisateur alors qu'il est toujours en train de naviguer. votre cependant.
  2. Déclenchez le signal de déconnexion chaque fois qu'un utilisateur se connecte, juste pour être sûr. Créez également une tâche 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 sessions actives, auquel cas vous déclenchez le signal de déconnexion.

Ces solutions sont désordonnées et non idéales, mais c'est ce que vous pouvez faire de mieux, malheureusement.

0 votes

Cela ne répond toujours pas à la situation "la plupart du temps, les utilisateurs quittent simplement la page ou ferment le navigateur".

0 votes

Merci pour votre réponse, bien sûr envelopper le login/logout m'évite de Parcheando la source, j'aurais dû y penser moi-même. Une petite correction, cependant : si le signal est envoyé dans my_login avant que login ne soit appelé, l'utilisateur est toujours anonyme dans le gestionnaire de signal. Mieux (je ne pense pas que cela sera formaté correctement) : def my_login(request) : response = login(request) #fire un signal, ou équivalent return response Aussi, j'utilise déjà is_authenticated, j'avais juste le sentiment que j'aurais besoin de plus que cela. Jusqu'à présent, cependant, mon nouvel élément is_logged_in dans le modèle de données est resté inutilisé.

0 votes

De bons points tout autour. Je suppose que je n'ai pas vraiment abordé le is_logged_in Je suis désolé, je n'ai pas dû bien lire le message, mais j'ai mis à jour la réponse pour offrir toute l'aide possible dans ce domaine. C'est un problème un peu impossible, malheureusement.

1voto

Joel L Points 2209

La seule façon fiable (qui détecte également quand l'utilisateur a fermé le navigateur) est de mettre à jour un fichier last_request chaque fois que l'utilisateur charge une page.

Vous pourriez également avoir une requête AJAX périodique qui interroge le serveur toutes les x minutes si l'utilisateur a une page ouverte.

Ensuite, une seule tâche d'arrière-plan récupère la liste des utilisateurs récents, crée des tâches pour eux et efface les tâches des utilisateurs qui ne figurent pas dans cette liste.

0 votes

C'est le meilleur moyen de mesurer si un utilisateur est connecté ou non. Vous devrez essentiellement tenir une liste des utilisateurs connectés, vérifier leur dernier accès et décider d'un délai d'attente. Si vous fixez le délai d'attente à quelque chose comme 10 minutes, et si vous demandez à chaque page Web d'appeler une requête Ajax toutes les 5 minutes environ tant qu'une page est active, cela devrait permettre de maintenir le statut à jour.

1voto

Peter Rowell Points 12444

Pour déduire la déconnexion, par opposition à un clic explicite sur un bouton (ce que personne ne fait), il faut choisir une durée d'inactivité qui équivaut à une "déconnexion". phpMyAdmin utilise une valeur par défaut de 15 minutes, certains sites bancaires n'utilisent que 5 minutes.

La façon la plus simple de mettre cela en œuvre serait de modifier la durée de vie des cookies. Vous pouvez le faire pour l'ensemble de votre site en spécifiant settings.SESSION_COOKIE_AGE . Alternativement, vous pouvez le changer sur une base par utilisateur (basé sur un ensemble arbitraire de critères) en utilisant HttpResponse.setcookie() . Vous pouvez centraliser ce code en créant votre propre version de render_to_response() et lui faire fixer la durée de vie de chaque réponse.

0voto

Tomasz Zielinski Points 9300

En gros, vous pourriez utiliser un intergiciel pour cela. Cet intergiciel pourrait traiter les demandes et émettre un signal lorsque l'URL concernée est demandée. Il pourrait également traiter les réponses et envoyer un signal lorsque l'action donnée a réussi.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X