84 votes

Django: signal lorsque l'utilisateur se connecte?

Dans mon application Django, j'ai besoin de commencer à exécuter quelques tâches de fond périodiques lorsque qu'un utilisateur se connecte et les arrêter lorsqu'il se déconnecte, je recherche donc un moyen élégant de

  1. être notifié d'une connexion/déconnexion d'un utilisateur
  2. vérifier 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 possède pas cela et je suis réticent à modifier la source pour l'ajouter (de toute façon, je ne sais pas comment faire).

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

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 paraît pas évident. En outre (même si c'est plutôt pointilleux en termes de clarté du modèle de données), is_logged_in ne devrait pas être dans UserProfile, mais dans le modèle User.

Est-ce que quelqu'un peut penser à d'autres approches ?

4 votes

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

1 votes

Vous avez raison; réponse acceptée modifiée.

157voto

PhoebeB Points 2862

Vous pouvez utiliser un signal comme ceci (j'ai placé 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 meilleure solution que d'encapsuler l'appel à la connexion/déconnexion. Cela signifie également que si vous mettez en place de nouvelles façons de vous connecter - par exemple, des connexions Facebook/Twitter/OpenID - elles fonctionneront toujours.

9 votes

Au lieu de mettre cela dans models.py, je suggère plutôt de placer le code dans signals.py et de l'importer automatiquement dans le fichier __init__.py du module.

0 votes

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

13voto

ShZ Points 3431

Une option pourrait être d'encadrer 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)

Ensuite, utilisez ces vues dans votre code plutôt que celles de Django, et voilà.

En ce qui concerne la vérification du statut de connexion, c'est assez simple si vous avez accès à l'objet de requête ; vérifiez simplement l'attribut utilisateur de la requête pour voir s'ils sont un utilisateur enregistré ou un utilisateur anonyme, et bingo. Pour citer la documentation de Django :

if request.user.is_authenticated():
    # Faire quelque chose pour les utilisateurs connectés.
else:
    # Faire quelque chose pour les utilisateurs anonymes.

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

Édition :

Malheureusement, vous ne pourrez jamais obtenir la fonctionnalité User.is_logged_in() - c'est une limitation du protocole HTTP. Cependant, en faisant quelques hypothèses, vous pourriez vous en rapprocher.

Premièrement, pourquoi ne pouvez-vous pas obtenir cette fonctionnalité ? Eh bien, vous ne pouvez pas faire la différence entre quelqu'un qui ferme le navigateur, ou quelqu'un qui passe un certain temps sur une page avant d'en charger une autre. Il n'y a aucun moyen de savoir sur HTTP quand quelqu'un quitte réellement le site ou ferme le navigateur.

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

  1. Utilisez l'événement unload de Javascript pour savoir quand un utilisateur quitte une page. Vous devriez écrire une logique soigneuse pour vous assurer que vous ne déconnectez pas un utilisateur lorsqu'il navigue encore sur votre site.
  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 - lorsque 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 encombrantes et pas idéales, mais c'est la meilleure chose que vous puissiez faire, malheureusement.

0 votes

Cela ne résout toujours pas le problème de la situation où "la plupart du temps, les utilisateurs quittent simplement la page ou ferment le navigateur".

0 votes

Merci pour votre réponse, bien sûr l'enveloppe de connexion/déconnexion m'évite de patcher la source, j'aurais dû y penser moi-même. Une petite correction, cependant : Si le signal est envoyé dans my_login avant l'appel de connexion, l'utilisateur est toujours anonyme dans le gestionnaire de signal. Mieux (ne pense pas que cela sera formaté correctement) : def my_login(request): response = login(request) # déclencher 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 nouveau morceau de is_logged_in dans le modèle de données reste inutilisé.

0 votes

De bons points tout autour. Je suppose que je n'ai vraiment pas bien abordé les choses is_logged_in du tout (désolé pour ça, je suppose que je n'ai pas très bien lu le post), mais j'ai mis à jour la réponse pour offrir l'aide que je peux 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 lorsque l'utilisateur a fermé le navigateur) est de mettre à jour un champ dernière_requête chaque fois que l'utilisateur charge une page.

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

Ensuite, avoir un seul travail en arrière-plan qui obtient une liste des utilisateurs récents, crée des tâches pour eux et efface les tâches des utilisateurs non présents dans cette liste.

0 votes

C'est la meilleure façon de mesurer si un utilisateur est connecté ou non. Vous devrez essentiellement garder une liste des utilisateurs connectés et vérifier à quand remonte leur dernière connexion pour décider d'un délai d'attente. Si vous définissez le délai d'attente à 10 minutes, et que chaque page web appelle également une requête Ajax toutes les 5 minutes environ tant qu'une page est active, cela devrait maintenir le statut à jour.

1voto

Peter Rowell Points 12444

Inférer la déconnexion, plutôt que de les faire cliquer explicitement sur un bouton (que personne ne fait), signifie choisir un laps de temps d'inactivité équivalent à "déconnecté". phpMyAdmin utilise une valeur par défaut de 15 minutes, certains sites bancaires utilisent aussi peu que 5 minutes.

La manière la plus simple de mettre en œuvre cela serait de changer la durée de vie du cookie. Vous pouvez le faire pour l'ensemble de votre site en spécifiant settings.SESSION_COOKIE_AGE. Alternativement, vous pourriez le changer sur une base utilisateur par utilisateur (en fonction de certains critères arbitraires) en utilisant HttpResponse.setcookie(). Vous pouvez centraliser ce code en créant votre propre version de render_to_response() et en lui faisant définir la durée de vie sur chaque réponse.

0voto

Tomasz Zielinski Points 9300

Idée brute - vous pourriez utiliser un intergiciel pour cela. Cet intergiciel pourrait traiter les demandes et déclencher un signal lorsque l'URL pertinente est demandée. Il pourrait également traiter les réponses et déclencher un signal lorsque l'action donnée a réellement 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