Implementar funcionalidad de recuperación/reseteo de contraseña en Django

Hoy voy a hablaros de cómo implementar un sistema de recuperación de contraseña para usuarios de Django. Este framework ya dispone de mecanismos para ello, pero iremos un poco más allá personalizando el aspecto de las vistas implicadas en este proceso. Ésto que os pongo lo he probado con la versión 1.10 de Django.

Configuración del correo electrónico

Nuestro backend debe enviar correos electrónicos a los usuarios durante el proceso de reuperación de contraseña. Tanto si el envío de éstos emails se realizan desde sendmail como si utilizamos un servidor SMTP externo, ésto lo configuramos así:

** src / settings.py **


EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = '' # servicio de correo smtp
EMAIL_HOST_USER = '' # id de correo electrónico
EMAIL_HOST_PASSWORD = '' #password
EMAIL_PORT = 587
EMAIL_USE_TLS = True

Normalmente, si trabajáis con un sitio muy grande, tendréis que disponer de algún servicio tipo Sendgrid. Pero para sitios pequeños, cualquier buzón de Gmail sera suficiente.

Fichero url.py

Éstas son todas las rutas implicadas en el proceso. Por supuesto las podéis personalizar a vuestro gusto. Lo importante es definir bien las expresiones regulares y las vistas a las que apuntan.


url(r'^user/password/reset/$', django.contrib.auth.views.password_reset, {'post_reset_redirect' : '/user/password/reset/done/'}, name='password_reset'),
url(r'^user/password/reset/done/$',django.contrib.auth.views.password_reset_done),
url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', django.contrib.auth.views.password_reset_confirm, {'post_reset_redirect': '/user/reset/done/'}, name='password_reset_confirm'),
url(r'^user/reset/done/$', django.contrib.auth.views.password_reset_complete, name='password_reset_complete'),


La vistas de password_reset

Django tiene sus propios templates para éste proceso de recuperación de contraseña. Nosotros vamos a sobreescribirlos con los nuestros. Para ello, creamos una carpeta registration dentro de "templates" e incluímos los siguentes ficheros .html (observa que también se incluye el template del email):

passwordresetform.html


{% extends "registration/base.html" %}

{% block title %}Reset Password{% endblock %}

{% block content %}
<p>Especifique su email para enviarle las instrucciones para la recuperación de contraseña.</p>

<form action="" method="post">
    <div style="display:none">
        <input type="hidden" value="{{ csrf_token }}" name="csrfmiddlewaretoken">
    </div>
     {{ form.email.errors }}
    <p><label for="id_email">Dirección de correo electrónico:</label> {{ form.email }} </p>
    <br>
    <p><input type="submit" value="Recuperar contraseña" /></p>
</form>
{% endblock %}


passwordresetemail.html


{% load i18n %}
{% autoescape off %}
Está recibiendo este correo porque solicitó un cambio de contraseña en {{ site_name }}.

Por favor vaya a esta página y configure su nueva constreña
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
{% endblock %}

Su nombre de usuario en caso que lo haya olvidado: {{ user.username }}

Gracias por usar nuestro sitio!

JLA Asociados.

{% endautoescape %}


passwordresetdone.html



{% extends "registration/base.html" %}

{% block title %}Recuperación de contraseña exitosa{% endblock %}

{% block content %}
<p>Hemos enviado las instrucciones de como recuperar la contraseña a la dirección de correo electrónico especificada</p>
<p>Estarás recibiendo el correo proto.</p>
{% endblock %}



passwordresetconfirm.html



{% extends "registration/base.html" %}
{% block title %}Definir nueva contraseña{% endblock %}

{% block content %}
    {% if validlink %}
        <p>Por favor ingrese su nueva contraseña dos veces.<br />
          Así podremos verificar que es correcta.</p>
        <form action="" method="post">
            <div style="display:none">
                <input type="hidden" value="{{ csrf_token }}" name="csrfmiddlewaretoken">
            </div>
            <table>
                <tr>
                    <td>{{ form.new_password1.errors }}
                        <label for="id_new_password1">Nueva contraseña:</label></td>
                    <td>{{ form.new_password1 }}</td>
                </tr>
                <tr>
                    <td>{{ form.new_password2.errors }}
                        <label for="id_new_password2">Confirmar contraseña:</label></td>
                    <td>{{ form.new_password2 }}</td>
                </tr>
                <tr>
                    <td></td>
                    <td><input type="submit" value="Cambiar mi contraseña" /></td>
                </tr>
            </table>
        </form>
    {% else %}
        <h1>Recuperación de contraseña fallida</h1>
        <p>El vínculo de recuperación de contraseña es inválido, <br />
        Posiblemente ya ha sido usado <br />
        Por favor solicite una nueva contraseña.</p>
    {% endif %}
{% endblock %}



passwordresetcomplete.html



{% extends "registration/base.html" %}

{% block title %}Password reset complete{% endblock %}

{% block content %}
<p>Tu contraseña ha sido definida. Ya puedes iniciar sesión de nuevo</p>
<p><a href="/login">Iniciar sesión</a></p>
{% endblock %}


¡Y esto es todo!

Como complemento, os dejo dos enlaces con los que podréis ir más allá si lo necesitáis. Corresponden a la documentación oficial.

Documentación oficial

Código fuente de PasswordResetForm y SetPasswordForm