2008-11-23 14 views
47

Quiero agregar un poco de Ajax -niceness a mi sitio web con código Django.Autenticación Django y Ajax - URLs que requieren iniciar sesión

En mi código Django, uso el decorador @login_required de django.contrib.auth.decorators para marcar qué vista requiere autenticación. El comportamiento predeterminado cuando un usuario no autenticado hace clic en él es redirigirlo a la página de inicio de sesión y luego pasar la página de destino.

Lo que vi en algunos sitios, y realmente me gustó, es que cuando el usuario hace clic en un enlace que conduce a un lugar restringido a usuarios solo registrados, en vez de ser redirigido a una página de inicio de sesión, obtiene una ventana emergente (a través de JavaScript) pidiéndole que inicie sesión o se registre. No hay parte de redirección, por lo que no es necesario que el usuario use la tecla "volver" si él/ella decide que realmente no le gusta el sitio web lo suficiente como para perder el tiempo registrando.

Por lo tanto, la pregunta es: ¿cómo administrarías la tarea de marcar automáticamente algunos enlaces como "restringidos" para que JavaScript pueda manejar su evento onclick y mostrar una ventana emergente "por favor inicie sesión"?

Respuesta

53

I Estoy enfrentando el mismo problema, y, como usted, me gustaría que un simple decorador cubra una vista Django ajax para manejar la autenticación de la misma manera que tengo otras vistas. Un enfoque que me parece prometedor es utilizar un decorador así junto con JavaScript que busque un cierto valor en la respuesta.

Aquí es primera proyecto revisado del decorador:

from functools import wraps 

def ajax_login_required(view_func): 
    @wraps(view_func) 
    def wrapper(request, *args, **kwargs): 
     if request.user.is_authenticated(): 
      return view_func(request, *args, **kwargs) 
     json = simplejson.dumps({ 'not_authenticated': True }) 
     return HttpResponse(json, mimetype='application/json') 
    return wrapper 

Aquí es la vista:

@ajax_login_required 
def ajax_update_module(request, module_slug, action): 
    # Etc ... 
    return HttpResponse(json, mimetype='application/json') 

Y aquí es el Javascript (jQuery):

$.post('/restricted-url/', data, function(json) { 
    if (json.not_authenticated) { 
     alert('Not authorized.'); // Or something in a message DIV 
     return; 
    } 
    // Etc ... 
}); 

EDITAR: Intenté usar functools.wraps, como se sugiere. Realmente no he usado este decorador en el código de trabajo, así que ten cuidado con posibles errores.

+6

Quizás valga la pena señalar que puede usar el decorador 'functools.wraps' alrededor de' def wrap' para evitar las asignaciones a 'wrap .__ doc__',' wrap .__ dict__', y 'wrap .__ name__' (que no es mencionado en esta respuesta, pero debe hacerse). Ver http://docs.python.org/library/functools.html#functools.wraps – quodlibetor

+2

@EricWalker ¿tal vez actualizar la publicación entonces? :) –

+0

@Anders: por favor, adelante y edite la respuesta para incorporar el cambio. No he trabajado en Python por un tiempo, y no quiero estropearlo. –

5

Parece una posibilidad de plantilla de página.

  1. Se podía pasar una LINK_VIA (o algo así) que proporcione como onClick="return popup(this, 'arg')" o None. Cada enlace sería <A HREF="link" {{LINK_VIA}}>some text</a>.

    • Para sesiones anónimas, LINK_VIA tiene un valor.
    • Para iniciar sesión en las sesiones, es LINK_VIA Ninguno
  2. Se podría utilizar un comunicado {% if %} alrededor de sus <A HREF=...> etiquetas. Esto parece prolijo

  3. Puede escribir su propia etiqueta personalizada con {% link_via %}. No estoy lo suficientemente familiarizado con esto, pero puede proporcionar el enlace y el texto como cadenas y su etiqueta puede generar uno de dos tipos de enlaces.

+0

Lo que me pregunto es cómo determine desde el punto de vista de la plantilla si la vista a la que conduce el vínculo está decorada con @login_required o no ... – kender

+0

La plantilla no tiene enlaces aleatorios. Tiene enlaces específicos que ha diseñado y codificado específicamente en la plantilla. Te sugiero que cambies cada enlace que especificaste en la plantilla. –

5

Estoy de acuerdo con S.Lott

Hacer una marca en la plantilla, si el usuario está conectado, sólo hay que poner el enlace, como de costumbre, si no, poner algo como

<a href="{{link}}" onclick="return login_popup()"> 

donde login_popup devolvería false si el usuario dice cancel.

Esto probablemente podría hacerse mucho más fácil en Jinja2 a través de macros.

Si la plantilla no sabe qué URL requieren que el usuario inicie sesión, es probable que deba reconsiderar su diseño.

Si debe, supongo que puede hacer lo mismo que el django url dispatcher para descubrir la función de vista.
ver: django.core.urlresolvers

una vez que hayas elegido la función de vista puedes comprobar si está decorada con @login_required.

Esto se haría probablemente en una etiqueta personalizada.
Si utiliza Jinja2, que no tendrá la etiqueta, simplemente implementar la función y exponerlo al medio ambiente, es simple pero vas a tener que hacer un poco de lectura en la API de Jinja2)

+0

Llego tarde a la fiesta, pero sea lo que sea: esto no es ideal solo porque el usuario podría haber seguido un enlace directo a la página en cuestión, en cuyo caso la metodología emergente requerida para iniciar sesión aquí se romperá. –

+0

Esto me lastima los ojos. –

1

Aquí se propone versión del decorador con una envoltura .__ doc__, envuelva .__ name__

from functools import wraps 

def ajax_login_required(function): 
    def wrap(request, *args, **kwargs): 
     if request.user.is_authenticated(): 
      return function(request, *args, **kwargs) 
     json = simplejson.dumps({ 'not_authenticated': True }) 
     return HttpResponse(json, mimetype='application/json') 
    wrap.__doc__ = function.__doc__ 
    wrap.__name__ = function.__name__ 
    return wrap 
0

generó a partir de Eric Walker's solución, pero para Django 2,0

# Standard Imports 
import functools 
import django.http 

def ajax_login_required(view_func): 
    @functools.wraps(view_func) 
    def wrapper(request, *args, **kwargs): 
     if request.user.is_authenticated: 
      return view_func(request, *args, **kwargs) 

     return django.http.JsonResponse('Unauthorized', status=401, safe=False) 

    return wrapper 
Cuestiones relacionadas