2010-04-29 14 views
45

Me gustaría que el formulario de inicio de sesión (AuthenticationForm de django.contrib.auth) aparezca en todas las páginas de mi sitio si el usuario no está conectado. Cuando el usuario inicia sesión, será redirigido a la misma página. Si hay un error, el error se mostrará en la misma página con el formulario.Poner un formulario de inicio de sesión django en cada página

Supongo que necesitará un procesador de contexto para proporcionar el formulario a cada plantilla. Pero, ¿entonces también necesitaría todas las vistas para manejar el formulario publicado? ¿Esto significa que necesitas crear algún middleware? Estoy un poco perdido.

¿Hay una forma aceptada de hacer esto?

Respuesta

33

Ok, finalmente encontré la manera de hacerlo, aunque estoy seguro de que hay mejores formas. Creé una nueva clase de middleware llamada LoginFormMiddleware. En el método process_request, manejar la forma más o menos la forma en la vista de autenticación de inicio de sesión hace:

class LoginFormMiddleware(object): 

    def process_request(self, request): 

     # if the top login form has been posted 
     if request.method == 'POST' and 'is_top_login_form' in request.POST: 

      # validate the form 
      form = AuthenticationForm(data=request.POST) 
      if form.is_valid(): 

       # log the user in 
       from django.contrib.auth import login 
       login(request, form.get_user()) 

       # if this is the logout page, then redirect to/
       # so we don't get logged out just after logging in 
       if '/account/logout/' in request.get_full_path(): 
        return HttpResponseRedirect('/') 

     else: 
      form = AuthenticationForm(request) 

     # attach the form to the request so it can be accessed within the templates 
     request.login_form = form 

Ahora bien, si usted tiene instalado el procesador de solicitud de contexto, se puede acceder al formulario con:

{{ request.login_form }} 

Tenga en cuenta que se agregó un campo oculto 'is_top_login_form' al formulario para poder distinguirlo de otros formularios publicados en la página. Además, la acción de formulario es "." en lugar de la vista de inicio de sesión de autenticación.

+0

Gracias, me estaba arrancando el pelo tratando de hacer algo similar (pero a través de un procesador de contexto). – LaundroMat

+0

Solución realmente limpia, pero ¿tiene que verificar todos los demás formularios publicados para asegurarse de que no es is_top_login_form antes de intentar validar el formulario, o me falta alguna parte? Me refiero a la vista más adelante si tiene alguna. otra forma tiene que ser si reqeust.POST y no 'is_top_lo gin_form 'en request.POST? – niklasdstrom

+5

Los formularios Django toman un argumento ['prefix'] (https://docs.djangoproject.com/en/dev/ref/forms/api/#prefixes-for -forms), que puede usar en lugar del campo oculto. p.ej. 'form = AuthenticationForm (prefix =" login ", data = request.POST)'. – Alasdair

6

La forma más sencilla es probablemente para poner el formulario en forma manual en una plantilla base, así:

{% if user.is_authenticated %} 
    <form action="{% url login %}" method="POST">{% csrf_token %} 
     <input id="username-field" name="username" type="text" /> 
     <input id="password-field" name="password" type="password" /> 
     <button type="submit">Login</button> 
    </form> 
{% else %} 
    {# display something else here... #} 
{% endif %} 

y luego acaba de escribir una vista conectado a una URL llamado "login" para manejar la forma como usted lo haría normalmente (usando un objeto de formulario que coincida con la forma anterior). Haga que la vista redireccione al request.META['HTTP_REFERER'] para mostrarla en la misma página que la que envió.

Este enfoque evita middleware o la necesidad de hacer un formulario disponible para cada plantilla a través del contexto.

Actualización: Hay algunos problemas con este enfoque; necesito pensar un poco más. Espero que al menos te lleve en la dirección correcta.

+4

Su formulario debe incluir '{% csrf_token%}' si está utilizando una versión reciente de trunk y si está publicando en la vista django.contrib.auth que generalmente maneja el inicio de sesión. –

+0

Cualquiera sabe cómo mostrar los mensajes de error en una plantilla cuando usa el valor predeterminado 'AuthenticationForm'? (No estoy usando 'form.as_p'. En su lugar, estoy usando etiquetas como' form.username' para tener más control sobre la personalización. –

+2

'{{form.errors}}', '{{form.non_field_errors} } ', y' {{formulario .errors}} ' –

23

Usando django.contrib.auth, puede poner el código del formulario en la plantilla base de este modo:

<form method="post" action="{% url auth_login %}"> 
    {% csrf_token %} 
    <p><label for="id_username">Username:</label> <input id="id_username" type="text" name="username" maxlength="30" /></p> 
    <p><label for="id_password">Password:</label> <input type="password" name="password" id="id_password" /></p> 

    <input type="submit" value="Log in" /> 
    <input type="hidden" name="next" value="" /> 
</form> 

Todo lo que necesita hacer es modificar el siguiente valor de lo que en lugar de:

<input type="hidden" name="next" value="" /> 

ahora será:

<input type="hidden" name="next" value="{{ request.get_full_path }}" /> 

Para acceder al objeto de solicitud, asegúrese de incluir

'django.core.context_processors.request' 

en su plantilla de procesadores de contexto. De esta forma, no tiene que escribir ningún procesador de contexto para los inicios de sesión ya que está utilizando las vistas incorporadas de Django.

+4

Lo siento, me olvidé por completo de esta pregunta. Gracias por esta solución. No conocía el campo" siguiente ". Si el formulario valida, esto funciona perfectamente. Mi problema ahora es cómo manejar un error. Básicamente quiero volver a la vista "siguiente", pero con el formulario de inicio de sesión en el contexto. – asciitaxi

2

Sobre la base de la respuesta de asciitaxi, i utilizar estas clases middleware para iniciar sesión y cierre de sesión:

class LoginFormMiddleware(object): 
    def process_request(self, request): 
     from django.contrib.auth.forms import AuthenticationForm 
     if request.method == 'POST' and request.POST.has_key('base-account') and request.POST['base-account'] == 'Login': 
      form = AuthenticationForm(data=request.POST, prefix="login") 
      if form.is_valid(): 
       from django.contrib.auth import login 
       login(request, form.get_user()) 
      request.method = 'GET' 
     else: 
      form = AuthenticationForm(request, prefix="login") 
     request.login_form = form 

class LogoutFormMiddleware(object): 
    def process_request(self, request): 
     if request.method == 'POST' and request.POST.has_key('base-account') and request.POST['base-account'] == 'Logout': 
      from django.contrib.auth import logout 
      logout(request) 
      request.method = 'GET' 

Un esto en mi base de la plantilla:

{% if not request.user.is_authenticated %} 
    <form action="" method="post"> 
     {% csrf_token %} 
     <p id="login"> 
      {{ request.login_form.non_field_errors }} 
      {% for field in request.login_form %} 
       {{ field.errors }} 
       {{ field.label_tag}}: {{ field }} 
      {% endfor %} 
      <input type="submit" name="base-account" value="Login" /> 
     </p> 
    </form> 
{% else %} 
    <form action="" method="post"> 
     {% csrf_token %} 
     <p id="logout">Logged in as <b>{{ request.user.username }}</b>. 
      <input type="submit" name="base-account" value="Logout" /> 
     </p> 
    </form> 
{% endif %} 

Observaciones:

  • Las líneas request.method = 'GET' son necesarias para sitios con otros formularios. Parece un poco incómodo, pero funciona bien.
  • Dado que este se muestra en todas las páginas, no necesito el caso especial de cierre de sesión más, porque simplemente no necesito una página de cierre de sesión separada
  • Necesito un poco de distinción de mi formulario de inicio de sesión/cierre de sesión antes de comprobar si es válido (llamando así a la clase AuthenticationForm. De lo contrario, habrá errores cuando se trate de páginas con más formularios. Por lo tanto, utilizo el valor del botón Enviar para seleccionar los casos relevantes
+0

¿Parece que esto funciona con vistas basadas en clase para usted? He estado usando el código de Asciitaxi para el desarrollo, pero ahora que estoy cambiando a las vistas basadas en clases, parece que el middleware se está rompiendo. Más bien, el middleware anterior funciona bien, pero el resto de la solicitud se destruye y se devuelve una httpresponse en blanco (sin errores, sin vista renderizada). Básicamente tengo que hacer clic en 'iniciar sesión', luego obtener una página en blanco y luego volver a solicitar manualmente la URL en cuestión. Pregunta detallada: [http://stackoverflow.com/questions/16156899/django-loginformmiddleware-breaks-with-class-based-views] –

+0

¿De dónde obtiene los datos del formulario? ¿Es de una forma o modelo? No veo que se explique. – Jam1

Cuestiones relacionadas