2010-01-02 25 views
62

En mi aplicación Django, necesito para empezar a ejecutar algunos trabajos periódicos de los antecedentes cuando un usuario inicia sesión y dejar de funcionar cuando el usuario cierra la sesión, así que estoy buscando una manera elegante deDjango: ¿señal cuando el usuario inicia sesión?

  1. ser notificado de un inicio de sesión de usuario/cierre de sesión
  2. consulta de estado de inicio de sesión de usuario

Desde mi punto de vista, la solución ideal sería

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

Django 1.1.1 no tiene que y yo soy reacio a parchear la fuente y añadirlo (no estoy seguro de cómo hacerlo, de todos modos) .

como una solución temporal, he añadido un campo booleano is_logged_in al modelo PerfilUsuario que se elimina de forma predeterminada, se establece la primera vez que el usuario realiza la página de destino (definido 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 cerrar sesión, puedo borrar el indicador, pero la mayoría de las veces, los usuarios simplemente abandonan la página o cierran el navegador; limpiar la bandera en estos casos no me parece sencillo. Además (eso es más bien nitidez de modelo de datos), is_logged_in no pertenece al perfil de usuario, sino al modelo de usuario.

¿Alguien puede pensar en enfoques alternativos?

+4

favor consideren seleccionar una nueva respuesta. El aceptado actualmente es una opción muy pobre a la luz de la señal agregada en 1.3. – Bryson

+1

Tienes razón; cambió la respuesta aceptada. – ssc

Respuesta

125

Se puede utilizar una señal como ésta (pongo mina 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) 

Ver documentos django: https://docs.djangoproject.com/en/dev/ref/contrib/auth/#module-django.contrib.auth.signals y aquí http://docs.djangoproject.com/en/dev/topics/signals/

+8

Ahora que Django 1.3 ofrece estas señales, es una * mucho * mejor solución que completar la llamada para iniciar/cerrar sesión. También significa que si configura nuevas formas de iniciar sesión, por ejemplo, inicios de sesión de Facebook/Twitter/OpenID, seguirán funcionando. –

+8

En lugar de poner eso en 'models.py' en su lugar, sugiero colocar el código en' signals.py' e importarlo automáticamente en el archivo '__init __. Py' de los módulos. –

+0

cómo utilizar esta señal para ejecutar javascript al iniciar sesión y cerrar sesión? –

12

Una opción podría ser ajustar las vistas de inicio y 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, utilice estas vistas en su código en lugar de Django, y listo.

Con respecto a consultar el 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 el usuario anónimo, y bingo. Para citar el Django documentation:

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

Si usted no tiene acceso al objeto de solicitud, a continuación, determinar si el usuario actual se ha identificado que va a ser difícil.

Editar:

Desafortunadamente, usted nunca será capaz de obtener User.is_logged_in() funcionalidad - es una limitación del protocolo HTTP. Sin embargo, si hace algunas suposiciones, es posible que pueda acercarse a lo que desea.

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

lo que tiene dos opciones aquí que no son perfectos:

  1. unload caso de usar JavaScript para atrapar a cuando un usuario abandona 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án navegando en su sitio, sin embargo.
  2. Activa la señal de cierre de sesión cada vez que un usuario inicia sesión, solo para estar seguro. Cree también un trabajo cron que se ejecute con bastante frecuencia para eliminar las sesiones caducadas: cuando se elimina una sesión caducada, compruebe que el usuario de la sesión (si no es anónimo) no tiene más sesiones activas, en cuyo caso activa la señal de cierre de sesión.

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

+0

Esto todavía no aborda la situación "la mayoría de las veces, los usuarios simplemente abandonan la página o cierran el navegador". –

+0

Gracias por su respuesta, por supuesto, envolver iniciar/cerrar sesión me ahorra el parcheo de la fuente, aunque debería haberlo hecho. Sin embargo, una pequeña corrección: si la señal se envía en my_login antes de que se invoca el inicio de sesión, el usuario sigue siendo anónimo en el controlador de señal. Mejor (no creo que esto será formateado correctamente): def my_login (petición): respuesta = login (request) #fire una señal, o equivalente respuesta retorno Además, estoy usando ya is_authenticated, me solo tenía la sensación de que necesitaría más que eso. Hasta ahora, sin embargo, mi nueva pieza is_logged_in en el modelo de datos se encuentra allí sin usar. – ssc

+0

Puntos buenos por todos lados. Supongo que realmente no me encargué del tema 'is_logged_in' (disculpas, supongo que no hice un buen trabajo al leer la publicación), pero actualicé la respuesta para ofrecer toda la ayuda que pueda. en este área. Es un problema un poco imposible, desafortunadamente. – ShZ

0

idea áspera: podría utilizar middleware para esto. Este middleware podría procesar solicitudes y emitir señal cuando se solicite una URL relevante. También podría procesar las respuestas y la señal de fuego cuando la acción dada realmente sucedió.

1

La única manera 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 podría tener una solicitud AJAX periódica que haga ping al servidor cada x minutos si el usuario tiene una página abierta.

Luego tenga un trabajo de fondo simple 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.

+0

Esta es la mejor manera de medir si un usuario está conectado o no. Básicamente, tendrá que mantener una lista de usuarios conectados y comprobar cuál fue su último acceso y decidir sobre un tiempo de espera. Si configura el tiempo de espera en algo así como 10 minutos, y luego también hace que cada página web llame a una solicitud de Ajax cada 5 minutos aproximadamente mientras una página está activa, eso debería mantener el estado actualizado. –

1

Inferir el cierre de sesión, en lugar de tener que hacer clic explícitamente en un botón (que nadie lo 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 duración de la cookie. Puede hacer esto para todo su sitio especificando settings.SESSION_COOKIE_AGE. Alternativamente, puede cambiarlo por usuario (según algunos criterios arbitrarios) utilizando HttpResponse.setcookie(). Puede centralizar este código creando su propia versión de render_to_response() y configurándola para cada respuesta.

0

Puede usar la siguiente manera:

from django.contrib.auth.signals import user_logged_out, user_logged_in 

@login_required 
def user_logout(request): 
    logout(request) 
    user_logged_out() 
    return redirect('post_list') 
2

Además de @PhoebeB respuesta: también puede usar el decorador @receiver así:

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 pones en signals.py en su directorio de aplicaciones, a continuación, añadir esto a app.py:

def ready(self): 
    import app_name.signals` 
Cuestiones relacionadas