Django: señal cuando el usuario inicia sesión?

En mi aplicación Django, debo comenzar a ejecutar algunos trabajos de fondo periódicos cuando un usuario inicia sesión y deja de ejecutarlos cuando el usuario cierra sesión, por lo que estoy buscando una manera elegante de

  1. ser notificado de un usuario de inicio / cierre de sesión
  2. consultar el estado de inicio de sesión del usuario

Desde mi perspectiva, la solución ideal sería

  1. una señal enviada por cada django.contrib.auth.views.login y ... views.logout
  2. un método django.contrib.auth.models.User.is_logged_in() , análogo a ... User.is_active() o ... User.is_authenticated()

Django 1.1.1 no tiene eso y estoy reacio a parchear la fuente y agregarla (no estoy seguro de cómo hacerlo, de todos modos).

Como solución temporal, agregué un campo booleano is_logged_in al modelo UserProfile que se borra de manera predeterminada, se establece la primera vez que el usuario llega a la página de destino (definida por LOGIN_REDIRECT_URL = '/' ) y se consulta en las solicitudes posteriores. Lo agregué a UserProfile, por lo que no tengo que derivar y personalizar el modelo de Usuario incorporado solo para ese propósito.

No me gusta esta solución. Si el usuario hace clic explícitamente en el botón de cierre de sesión, puedo borrar la bandera, pero la mayoría de las veces, los usuarios simplemente abandonan la página o cierran el navegador; Borrar la bandera en estos casos no me parece fácil. Además (aunque se trata más bien de la nitidez del modelo de datos), is_logged_in no pertenece al is_logged_in usuario, sino al modelo de usuario.

¿Alguien puede pensar en enfoques alternativos?

Puedes usar una señal como esta (pongo la mía en 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) 

Consulte django docs: https://docs.djangoproject.com/en/dev/ref/contrib/auth/#module-django.contrib.auth.signals y aquí http://docs.djangoproject.com/en/dev/ temas / señales /

Una opción podría ser ajustar las vistas de inicio / cierre de sesión de Django con las suyas. Por ejemplo:

 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) 

A continuación, utiliza estas vistas en su código en lugar de Django, y listo.

Con respecto a la consulta del estado de inicio de sesión, es bastante simple si tiene acceso al objeto de solicitud; simplemente revise el atributo de usuario de la solicitud para ver si es un usuario registrado o anónimo y bingo. Para citar la documentación de Django :

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

Si no tiene acceso al objeto de solicitud, será difícil determinar si el usuario actual ha iniciado sesión.

Editar:

Desafortunadamente, nunca podrá obtener la funcionalidad User.is_logged_in() , es una limitación del protocolo HTTP. Sin embargo, si realiza algunas suposiciones, es posible que pueda acercarse a lo que desea.

Primero, ¿por qué no puedes obtener esa funcionalidad? Bueno, no puedes notar la diferencia entre alguien que cierra el navegador o alguien que pasa un tiempo en una página antes de buscar uno nuevo. No hay forma de saber a través de HTTP cuando alguien realmente abandona el sitio o cierra el navegador.

Así que tienes dos opciones aquí que no son perfectas:

  1. Utilice el evento de unload de Javascript para detectar cuándo un usuario está dejando una página. Tendría que escribir una lógica cuidadosa para asegurarse de que no está desconectando a un usuario cuando todavía está navegando por su sitio.
  2. Encienda la señal de cierre de sesión cada vez que un usuario inicie sesión, solo para estar seguro. También cree un trabajo cron que se ejecute con bastante frecuencia para eliminar las sesiones caducadas: cuando se borra una sesión caducada, verifique que el usuario de la sesión (si no es anónimo) no tenga más sesiones activas, en cuyo caso se activará la señal de cierre de sesión.

Estas soluciones son desordenadas y no ideales, pero desafortunadamente son lo mejor que puedes hacer.

Además de la respuesta de @PhoebeB: también puedes usar el decorador @receiver esta manera:

 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..` 

Y si lo signals.py en signals.py en el signals.py de tu aplicación, entonces agrega esto a app.py :

 def ready(self): import app_name.signals` 

La única forma confiable (que también detecta cuando el usuario ha cerrado el navegador) es actualizar el campo last_request cada vez que el usuario carga una página.

También puede tener una solicitud periódica de AJAX que haga ping al servidor cada x minutos si el usuario tiene una página abierta.

Luego, tenga un solo trabajo en segundo plano que obtenga una lista de usuarios recientes, cree trabajos para ellos y borre los trabajos para los usuarios que no están en esa lista.

Inferir el cierre de sesión, en lugar de tener que hacer clic explícitamente en un botón (lo que nadie hace), significa elegir una cantidad de tiempo de inactividad que equivale a “desconectado”. phpMyAdmin usa un valor predeterminado de 15 minutos, algunos sitios bancarios usan tan solo 5 minutos.

La forma más sencilla de implementar esto sería cambiar la vida útil de la cookie. Puedes hacer esto para todo tu sitio especificando la settings.SESSION_COOKIE_AGE . Alternativamente, puede cambiarlo por usuario (según un conjunto arbitrario de criterios) utilizando HttpResponse.setcookie() . Puede centralizar este código creando su propia versión de render_to_response() y haciendo que establezca la vida útil de cada respuesta.

Una idea aproximada: podrías usar middleware para esto. Este middleware podría procesar solicitudes y disparar señales cuando se solicite una URL relevante. También podría procesar las respuestas y la señal de disparo cuando una acción determinada realmente tuvo éxito.

una solución rápida para esto sería, en el _ _ init _ _.py de su aplicación, coloque el siguiente código:

 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....')