Django База [2023]: Форма восстановления пароля #23
В этой статье мы рассмотрим создание формы для восстановления пароля и процесс отправки письма на указанный email во время восстановления пароля в Django.
Если вы хотите выразить благодарность автору сайта, статей и курса по Django, вы можете сделать это по ссылке ниже:
В предыдущей статье мы рассматривали, как работать с SMTP-сервером для добавления возможности отправки писем с сайта.
Создание формы для запроса на восстановление пароля и установки нового пароля
В файл forms.py нашего приложения system в самый низ добавим следующий фрагмент кода:
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
class UserForgotPasswordForm(PasswordResetForm):
"""
Запрос на восстановление пароля
"""
def __init__(self, *args, **kwargs):
"""
Обновление стилей формы
"""
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs.update({
'class': 'form-control',
'autocomplete': 'off'
})
class UserSetNewPasswordForm(SetPasswordForm):
"""
Изменение пароля пользователя после подтверждения
"""
def __init__(self, *args, **kwargs):
"""
Обновление стилей формы
"""
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs.update({
'class': 'form-control',
'autocomplete': 'off'
})
В этих формах мы просто настраиваем стиль под Bootstrap, потому что в системе аутентификации Django нас все устраивает. Наследуемся от PasswordResetForm и SetPasswordForm.
В одном из уроков мы уже использовали SetPasswordForm, но в нашем случае мы будем использовать наследование для формы, которая используется неавторизованными пользователями сайта.
Создаем необходимые представления для работы форм
Во views.py нашего приложения system в самый низ добавим следующие представления:
from django.contrib.auth.views import PasswordResetView, PasswordResetConfirmView
from django.urls import reverse_lazy
from django.contrib.messages.views import SuccessMessageMixin
from .forms import UserForgotPasswordForm, UserSetNewPasswordForm
class UserForgotPasswordView(SuccessMessageMixin, PasswordResetView):
"""
Представление по сбросу пароля по почте
"""
form_class = UserForgotPasswordForm
template_name = 'system/user_password_reset.html'
success_url = reverse_lazy('home')
success_message = 'Письмо с инструкцией по восстановлению пароля отправлена на ваш email'
subject_template_name = 'system/email/password_subject_reset_mail.txt'
email_template_name = 'system/email/password_reset_mail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Запрос на восстановление пароля'
return context
class UserPasswordResetConfirmView(SuccessMessageMixin, PasswordResetConfirmView):
"""
Представление установки нового пароля
"""
form_class = UserSetNewPasswordForm
template_name = 'system/user_password_set_new.html'
success_url = reverse_lazy('home')
success_message = 'Пароль успешно изменен. Можете авторизоваться на сайте.'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Установить новый пароль'
return context
В первом представлении UserForgotPasswordView:
- Функционал для запроса пароля, где мы вводим свой email адрес.
- Используем форму UserForgotPasswordForm.
- Наследуемся от PasswordResetView представления, встроенного в Django.
- Указываем соответствующие шаблоны как для формы, так и для писем.
- Подмешиваем миксин SuccessMessageMixin для оповещения пользователя об успешной отправке письма с инструкцией по восстановлению пароля.
Во втором представлении UserPasswordResetConfirmView:
- Функционал для ввода нового пароля.
- Используем форму UserSetNewPasswordForm.
- Наследуемся от PasswordResetConfirmView представления, встроенного в Django.
- Указываем соответствующие шаблоны для формы.
- Подмешиваем миксин SuccessMessageMixin для оповещения пользователя об успешной смене пароля.
Обработка в urls.py созданных представлений
В файле urls.py нашего приложения system дополним ссылки новыми строками:
from django.urls import path
from .views import ProfileUpdateView, ProfileDetailView, UserRegisterView, UserLoginView, UserPasswordChangeView, UserForgotPasswordView, UserPasswordResetConfirmView
urlpatterns = [
path('user/edit/', ProfileUpdateView.as_view(), name='profile_edit'),
path('user/<str:slug>/', ProfileDetailView.as_view(), name='profile_detail'),
path('register/', UserRegisterView.as_view(), name='register'),
path('login/', UserLoginView.as_view(), name='login'),
path('password-change/', UserPasswordChangeView.as_view(), name='password_change'),
path('password-reset/', UserForgotPasswordView.as_view(), name='password_reset'),
path('set-new-password/<uidb64>/<token>/', UserPasswordResetConfirmView.as_view(), name='password_reset_confirm'),
]
В примере выше, мы добавили созданные представления.
В url для представления UserPasswordResetConfirmView: <uidb64>/<token>
- это необходимый токен для подтверждения пользователя для смены пароля, который придет на email адрес.
Создаем шаблоны для форм и писем
В templates/system я создам файлы: user_password_reset.html и user_password_set_new.html, а также папку email, а внутри папки email два файла: password_reset_mail.html и password_subject_reset_mail.txt
В файл user_password_reset.html добавим следующую разметку:
{% extends 'main.html' %}
{% block content %}
<div class="card mb-3 border-0 nth-shadow">
<div class="card-body">
<div class="card-title nth-card-title">
<h4>Запрос на восстановление пароля на сайте</h4>
</div>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-dark mt-2">Восстановить пароль</button>
</form>
</div>
</div>
{% endblock %}
В файл user_password_set_new.html следующую разметку:
{% extends 'main.html' %}
{% block content %}
<div class="card mb-3 border-0 nth-shadow">
<div class="card-body">
<div class="card-title nth-card-title">
<h4>Установить новый пароль</h4>
</div>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-dark mt-2">Подтвердить</button>
</form>
</div>
</div>
{% endblock %}
В файл email/password_reset_mail.html добавим следующую разметку:
{% autoescape off %}
Вы получили это электронное письмо, потому что запросили сброс пароля для своей учетной записи пользователя на {{ site_name }}.
Перейдите на следующую страницу и выберите новый пароль:
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
{% endblock %}
Ваше имя пользователя, если вы забыли: {{ user.get_username }}
Спасибо за использование нашего сайта!
The {{ site_name }} team
{% endautoescape %}
И в файл email/password_subject_reset_mail.txt добавим текст темы письма:
Инструкция по восстановлению пароля на сайте
Настройка settings.py для корректной работы отправки писем
Нам необходимо дополнить установленные приложения в Django INSTALLED_APPS, добавив django.contrib.sites
и установить новое свойство SITE_ID
:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sites',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'modules.blog.apps.BlogConfig',
'modules.system.apps.SystemConfig',
'mptt',
'debug_toolbar',
]
SITE_ID = 1
Приминение миграций от django.contrib.sites
Также нам необходимо применить новые миграции, добавленные приложением django.contrib.sites
с помощью команды py manage.py migrate
.
Результат:
(venv) PS C:\Users\Razilator\Desktop\Base\backend> py manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions, sites, system
Running migrations:
Applying sites.0001_initial... OK
Applying sites.0002_alter_domain_unique... OK
(venv) PS C:\Users\Razilator\Desktop\Base\backend>
Добавление адреса в админ-панеле
Теперь нам необходимо указать правильный домен, в нашем случае это локальная машина. Переходим в админку, в раздел сайты, изменяем существующий example.com:
Если вы используете доменное имя, то вписывайте его.
Проверяем работу формы по восстановлению пароля
Переходим на страницу для запроса инструкции по восстановлению: http://127.0.0.1:8000/password-reset/
Отлично, нам этом работа завершена. Надеюсь у вас все получилось!