Django 33. Сброс пароля пользователя
В этом посте реализуем функционал сброса пароля пользователем.
Дополнительные материалы
Для скачивания материалов необходимо войти или зарегистрироваться
Файлы также можно получить в Telegram-боте по коду: 568363
Реклама
В посте "Django 13. Сброс пароля", мы уже писали сброс пароля, но то было для панели администратора.
В этом посте, реализуем сброс пароля для обычных пользователей.
Также для корректной работы, необходимо указать почтовый сервис.
Как его настроить я описывал в посте "Django 12. Настройка отправки почты".
А в постах "Docker 5.1 Почтовый сервер на Docker Mailserver - настройка домена" и "Docker 5.2 Почтовый сервер на Docker Mailserver - настройка и запуск" рассказывал как запустить свой почтовый сервер.
Для реализации сброса пароля, можно пойти простым или сложным путём.
Простой путь, это использовать уже встроенные в Django представления и формы, именно так мы и сделали в прошлый раз.
Достаточно в файле urls.py
указать паттерны:
from django.contrib.auth import views as auth_views
path('password-reset/',
auth_views.PasswordResetView.as_view(template_name='users/pass_reset.html'),
name='password-reset'),
path('password_reset_confirm/<uidb64>/<token>/',
auth_views.PasswordResetConfirmView.as_view(template_name='users/password_reset_confirm.html'),
name='password_reset_confirm'),
path('password_reset_complete/', auth_views.PasswordResetCompleteView.as_view(template_name='users/password_reset_complete.html'),
name='password_reset_complete'),
path('password-reset/done/', auth_views.PasswordResetDoneView.as_view(template_name='users/password_reset_done.html'),
name='password_reset_done'),
Указав собственные шаблоны.
Однако, в нашем случае, уже есть сброс пароля для админа и они будут конфликтовать.
Можно вовсе убрать сброс пароля для админ и оставить общий, но куда интереснее переопределить представления.
Этим и займёмся.
Переопределение форм.
Начнём с переопределения форм для того, чтобы добавить в них поддержку стилей из Bootstrap.
Откроем файл forms.py
в директории приложения user_app
.
Тут нам надо переопределить две формы: форму ввода электронной почты для сброса и форму ввода нового пароля.
Мы не будем переписывать их полностью, а просто наследуемся от них и изменим стандартные поля.
Код форм:
from django.contrib.auth import password_validation
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
class CustomPasswordResetForm(PasswordResetForm):
email = forms.EmailField(
label="Email",
max_length=254,
widget=forms.EmailInput(
attrs={'class': 'form-control',
'placeholder': 'Введите Email',
"autocomplete": "email"}
)
)
class CustomSetPasswordForm(SetPasswordForm):
error_messages = {
"password_mismatch": "Пароли не совпадают"
}
new_password1 = forms.CharField(
label='Новый пароль',
widget=forms.PasswordInput(
attrs={'class': 'form-control',
'placeholder': 'Введите новый пароль',
"autocomplete": "new-password"}
),
strip=False,
help_text=password_validation.password_validators_help_text_html(),
)
new_password2 = forms.CharField(
label='Подтверждение нового пароля',
strip=False,
widget=forms.PasswordInput(
attrs={'class': 'form-control',
'placeholder': 'Подтвердите новый пароль',
"autocomplete": "new-password"}
),
)
В первой форме CustomPasswordResetForm
, унаследованной от PasswordResetForm
, мы переопределили только поле email
.
Добавив класс из Bootstrap
и прописав текст внутри поля.
Во второй форме CustomSetPasswordForm
, унаследованной от SetPasswordForm
, мы переопределили два поля new_password1
и new_password2
, а так же прописали сообщение, если пароли не совпадают в поле error_messages
.
Переопределение представлений.
Откроем файл views.py
в директории приложения user_app
.
Тут мы создадим два собственных представления:
CustomPasswordResetView
- представление страницы, где пользователь вводит электронную почту для сброса. Наследуемся отPasswordResetView
.CustomUserPasswordResetConfirmView
- представление страницы на которую пользователь перейдёт из письма для ввода нового пароля. Наследуемся отPasswordResetConfirmView
.
Код представлений.
from django.contrib.auth.views import PasswordResetView, PasswordResetConfirmView
from user_app.forms import CustomPasswordResetForm, CustomSetPasswordForm
class CustomPasswordResetView(PasswordResetView):
template_name = 'user_app/password_reset.html'
email_template_name = 'user_app/password_reset_email.html'
form_class = CustomPasswordResetForm
success_url = reverse_lazy('user_app:password_reset_done')
class CustomUserPasswordResetConfirmView(PasswordResetConfirmView):
template_name = 'user_app/password_reset_confirm.html'
success_url = reverse_lazy('user_app:password_reset_complete')
form_class = CustomSetPasswordForm
В этих представлениях, переопределим следующие поля:
template_name
- файл шаблона.email_template_name
- файл шаблона для отправки по электронной почте. Только в представленииCustomPasswordResetView
.form_class
- класс с формой.success_url
- путь, куда будет перенаправлен пользователь после отправки формы.
Шаблоны страниц.
В директории с шаблонами приложения user_app
, необходимо создать пять файлов:
password_reset.html
- первая страница сброса пароля. На этой странице пользователь вводит свой email.password_reset_done.html
- вторая страница сброса пароля. На этой странице сообщаем пользователю, что письмо со ссылкой для сброса было отправлено на email.password_reset_confirm.html
- третья страница сброса пароля. На этой странице пользователь вводит новый пароль.password_reset_complete.html
- четвёртая и последняя страница сброса пароля. На этой странице сообщаем, что пароль был изменён.password_reset_email.html
- страница шаблона для электронной почты. Код страницы взят из стандартного Django-шаблона, с одним лишь изменением - ссылкой на которую перейдёт пользователь из письма. Если этот файл не изменить, то в письме будет ссылка на сброс пароля администратора, а нам не нужно, что бы пользователи знали адрес панели администратора.
password_reset.html и password_reset_confirm.html.
Стандартный шаблон формы:
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-danger mt-3">Запросить новый пароль</button>
</form>
Различия только в тексте кнопки. Вообще можно сделать один файл и передавать текст кнопки через контекст.
password_reset_done.html.
В этом шаблоне сообщаем, что письмо отправлено и добавляем кнопку возврата на главную страницу.
<div class="container mt-3">
<h2>Ссылка для смены пароля отправлена на вашу почту.</h2>
<a href="{% url 'blog:index' %}" class="btn btn-danger mt-3">Вернуться на главную</a>
</div>
password_reset_complete.html.
В этом шаблоне сообщаем, что пароль изменён и добавляем две кнопки: переход на главную и вход на сайт.
<div class="container mt-3">
<h2>Вы успешно обновили пароль</h2>
<p>Ваш пароль заменен на новый!</p>
<a href="{% url 'user_app:login' %}" class="btn btn-danger mt-3">Войти</a>
<a href="{% url 'blog:index' %}" class="btn btn-danger mt-3">Вернуться на главную</a>
</div>
password_reset_email.html.
Изменённый стандартный шаблон письма со ссылкой для восстановления пароля.
Изменить необходимо имя URL-шаблона в этой строке:
{{ protocol }}://{{ domain }}{% url 'user_app:password_reset_confirm' uidb64=uid token=token %}
Код шаблона:
{% load i18n %}{% autoescape off %}
{% blocktranslate %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktranslate %}
{% translate "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'user_app:password_reset_confirm' uidb64=uid token=token %}
{% endblock %}
{% translate 'Your username, in case you’ve forgotten:' %} {{ user.get_username }}
{% translate "Thanks for using our site!" %}
{% blocktranslate %}The {{ site_name }} team{% endblocktranslate %}
{% endautoescape %}
URL-паттерны страниц.
Откроем файл urls.py
и в список urlpatterns
добавим новые паттерны:
from django.contrib.auth import views as auth_views
path('password-reset/', views.CustomPasswordResetView.as_view(), name='password-reset'),
path('password_reset_confirm/<uidb64>/<token>/',
views.CustomUserPasswordResetConfirmView.as_view(),
name='password_reset_confirm'),
path('password_reset_complete/',
auth_views.PasswordResetCompleteView.as_view(template_name='user_app/password_reset_complete.html'),
name='password_reset_complete'),
path('password-reset/done/',
auth_views.PasswordResetDoneView.as_view(template_name='user_app/password_reset_done.html'),
name='password_reset_done'),
Последний штрих.
Осталось добавить ссылку на страницу сброса пароля в шаблон страницы входа.
Откроем файл login.html
и добавим следующую строчку где-нибудь внизу:
Забыли пароль? <a href="{% url 'user_app:password-reset' %}">Сбросить пароль</a>
Готово. Теперь пользователь сможет сбросить свой пароль.
Все статьи