Реализация алгоритма восстановления пароля

Курс по Django: https://stepik.org/a/183363

Архив проекта: 69_sitewomen.zip

На этом занятии мы приступим непосредственно к реализации механизма восстановления паролей, в соответствии со следующей схемой:

Вначале создадим шаблон формы password_reset_form.html для ввода E-mail адреса:

{% extends 'base.html' %}
 
{% block content %}
<h1>Восстановление пароля</h1>
 
<form method="post">
    {% csrf_token %}
{% for f in form %}
<p ><label class="form-label" for="{{ f.id_for_label }}">{{f.label}}: </label>{{ f }}</p>
<div class="form-error">{{ f.errors }}</div>
{% endfor %}
 
    <p ><button type="submit">Сбросить по E-mail</button></p>
</form>
 
{% endblock %}

И шаблон password_reset_done.html с информацией о сбросе пароля по E-mail (скопирован из register/password_reset_done.html и немного изменен):

{% extends "base.html" %}
{% load i18n %}
 
{% block content %}
<h1>Сброс пароля</h1>
<p >{% translate 'We’ve emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly.' %}</p>
<p >{% translate 'If you don’t receive an email, please make sure you’ve entered the address you registered with, and check your spam folder.' %}</p>
{% endblock %}

Далее, воспользуемся стандартными классами представлений PasswordResetView и PasswordResetDoneView, у которых укажем пути к нашим шаблонам. В файле users/urls.py пропишем следующие строчки:

    path('password-reset/', 
         PasswordResetView.as_view(template_name = "users/password_reset_form.html"),
         name='password_reset'),
    path('password-reset/done/',
         PasswordResetDoneView.as_view(template_name = "users/password_reset_done.html"),
         name='password_reset_done'),

Добавим в шаблон формы авторизации ссылку для восстановления пароля (в файл login.html):

<p ><a href="{% url 'users:password_reset' %}">Забыли пароль?</a></p>

Запустим веб-сервер, откроем окно авторизации и нажмем на ссылку «Забыли пароль?». Появляется форма ввода E-mail-адреса пользователя. Вводим E-mail и нажимаем на кнопку «Отправить по E-mail». После чего у нас выскакивает ошибка, что маршрут с именем password_reset_confirm не определен. Действительно, мы реализовали только первую часть схемы. Давайте пропишем и вторую.

Сформируем еще два шаблона:

- файл password_reset_confirm.html:

{% extends 'base.html' %}
 
{% block content %}
<h1>Новый пароль</h1>
 
<form method="post">
    {% csrf_token %}
    <div class="form-error">{{ form.non_field_errors }}</div>
    {% for f in form %}
    <p ><label class="form-label" for="{{ f.id_for_label }}">{{f.label}}: </label>{{ f }}</p>
    <div class="form-error">{{ f.errors }}</div>
    {% endfor %}
 
    <p ><button type="submit">Сохранить</button></p>
</form>
 
{% endblock %}

- файл password_reset_complete.html:

{% extends 'base.html' %}
 
{% block content %}
<h1>Пароль изменен</h1>
 
<p >Поздравляем! Вы успешно изменили пароль своего аккаунта. Используйте его для
  <a href="{% url 'users:login' %}">входа в систему</a>.</p>
 
{% endblock %}

И в файле users/urls.py, опять же, воспользуемся стандартными классами представлений PasswordResetConfirmView и PasswordResetCompleteView:

    path('password-reset/<uidb64>/<token>/', PasswordResetConfirmView.as_view(template_name="users/password_reset_confirm.html"), name='password_reset_confirm'),
    path('password-reset/complete/', PasswordResetCompleteView.as_view(template_name="users/password_reset_complete.html"), name='password_reset_complete'),

Однако если сейчас попробовать восстановить пароль, то получим ошибку:

NoReverseMatch at /users/password-reset/

Она произошла из-за того, что в стандартном шаблоне registration\password_reset_email.html используется тег:

{% url 'password_reset_confirm' uidb64=uid token=token %}

без указания пространства имен users. Поэтому я скопирую этот шаблон и подправлю в нем этот тег:

{{ protocol }}://{{ domain }}{% url 'users:password_reset_confirm' uidb64=uid token=token %}

И в файле users/urls.py пропишем этот новый шаблон для класса представления PasswordResetView следующим образом:

    path('password-reset/',
         PasswordResetView.as_view(
             template_name="users/password_reset_form.html",
             email_template_name="users/password_reset_email.html",
             success_url=reverse_lazy("users:password_reset_done")
         ),
         name='password_reset'),

Обратите внимание, что мы здесь же добавили параметр success_url с указанием пространства имен users. Иначе, при перенаправлении возникла бы та же ошибка.

Теперь все проходит, мы видим письмо со ссылкой для восстановления пароля и информационную страницу о его сбросе на почтовый ящик.

Прежде чем переходить по этой ссылке, мы сразу пропишем параметр success_url и у класса представления PasswordResetConfirmView:

    path('password-reset/<uidb64>/<token>/',
         PasswordResetConfirmView.as_view(
             template_name="users/password_reset_confirm.html",
             success_url=reverse_lazy("users:password_reset_complete")
         ),
         name='password_reset_confirm'),

(Значение параметра success_url можно посмотреть в самом классе PasswordResetConfirmView.)

Снова сделаем сброс пароля, переходим по ссылке в письме и попадаем на страницу изменения пароля. Вводим его, и он сохраняется для пользователя с указанным E-mail адресом.

Вот так, относительно несложно, можно реализовать восстановление паролей во фреймворке Django.

Курс по Django: https://stepik.org/a/183363

Видео по теме