Django 5 для начинающих

Прогресс по курсу:  9/1004

7.2 Использование системы аутентификации Django
4 из 7 шагов пройдено
0 из 11 баллов  получено

Мы успешно создали новую функцию регистрации, которая будет работать вместе с нашим существующим входом и выходом из системы. Не хватает только одного: добавить пользователям возможность сбрасывать свои пароли.

Нам нужна password_reset страница, на которой пользователь может ввести свой адрес электронной почты и получить криптографически защищенное электронное письмо с одноразовой ссылкой на страницу reset.

Если вы помните полный набор представлений и URL-адресов, предоставляемых auth приложением Django, уже есть несколько для сброса пароля:

accounts/login/ [name='login']
accounts/logout/ [name='logout']
accounts/password_change/ [name='password_change']
accounts/password_change/done/ [name='password_change_done']
accounts/password_reset/ [name='password_reset']
accounts/password_reset/done/ [name='password_reset_done']
accounts/reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/reset/done/ [name='password_reset_complete']

Шаблон по умолчанию для сброса пароля находится по адресу templates/registration/password_reset_form.html Добавим в него следующий код:

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

{% block title %}Forgot Your Password?{% endblock %}

{% block content %}
  <h1>Forgot your password?</h1>
  <p>Enter your email address below, and we'll email instructions for setting a new one.</p>

  <form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Send me instructions!">
  </form>
{% endblock %}

Если вы обновите страницу, http://127.0.0.1:8000/accounts/password_reset/ вы увидите:

Также нам нужно создать шаблон password_reset_done.html с информацией об удачной отправке письма:

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

{% block title %}Email Sent{% endblock %}

{% block content %}
  <h1>Check your inbox.</h1>
  <p>We've emailed you instructions for setting your password. You should receive the email shortly!</p>
{% endblock %}

Также создадим файл password_reset_confirm.html для смены пароля:

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

{% block title %}Enter new password{% endblock %}

{% block content %}

    {% if validlink %}

        <h1>Set a new password!</h1>
        <form method="POST">
            {% csrf_token %}
            {{ form.as_p }}
            <input type="submit" value="Change my password">
        </form>

    {% else %}

        <p>The password reset link was invalid, possibly because it has already been used. Please request a new password
            reset.</p>

    {% endif %}
{% endblock %}

После отправки вы будете перенаправлены на нашу последнюю страницу по умолчанию, предназначенную для завершения сброса пароля password_reset_complete.html:

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

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

{% block content %}
    <h1>Password reset complete</h1>
    <p>Your new password has been set. You can log in now on the <a href="{% url 'login' %}">log in page</a>.</p>
{% endblock %}

А теперь об интересном, кто-нибудь обратил внимание на то, что при регистрации мы не вводили e-mail адрес? Поэтому на данном этапе наше восстановление пароля работать не будет, попробуем это исправить в следующем шаге.


  • Комментария
Будьте вежливы и соблюдайте наши принципы сообщества. Пожалуйста, не оставляйте решения и подсказки в комментариях, для этого есть отдельный форум.
Оставить комментарий

У меня почему-то сброс пароля открывается в админке, а не на нашем сайте

Можете прислать структуру вашей папки templates?

@Илья_Перминов, Я же правильно понял, что шаблоны для регистрации должны находится в приложении accounts?

@Кислинский_Роман, Не совсем, у нас в файле настроек указано 

'DIRS': [BASE_DIR / 'templates']

Тоесть Django ожидает шаблоны в папке templates корневой директории проекта.

@Илья_Перминов, Cпасибо, разобрался)

Сейчас вот такая ошибка выходит, после того как жмешь сбросить пароль.

@Кислинский_Роман, А настройку отправки писем по электронной почте делали? https://stepik.org/lesson/973393/step/3?unit=980245

@Илья_Перминов, Да, делал через mail.ru, функция поделиться постом работает, письмо на мыло приходит

@Кислинский_Роман, Попробуйте переделать на gmail, погуглил и судя по всему, у mail.ru это довольно частая ошибка при отправке.

@Илья_Перминов, Хорошо, спасибо)

@Кислинский_Роман, У меня такое появилось по причине того, что я забыл создать шаблон
У вас это могло произойти еще по причине, что в этом куске ошибка

Надо {% extends "blog/base.html" %} в первой строке

@Кирилл_Смирнов, Скорее всего забыли создать шаблон password_reset_confirm.html

@Кирилл_Смирнов, Вы абсолютно правы, сегодня исправим это все! Очень извиняемся за это. На этапе написания данного шага запутались видимо в количество шаблонов))

@Илья_Перминов, Есть еще момент. На этом этапе очень тяжело догадаться, что этот файл должен называться password_reset_done.html

А так все отлично! Спасибо, что улучшаете курс!

@Кирилл_Смирнов, Вроде все поправили.

@Кислинский_Роман, @Илья Перминов решил проблему выпадения исключения SMTPSenderRefused с помощью добавления в setting.py: DEFAULT_FROM_EMAIL = ('YOUR_EMAIL_USER'). Все работает на mail.ru, письма приходят, пароль меняется. И все также работает функция поделится постом. Надеюсь кому-то помог

)) Оценила шутку с email ) Естественно, увидев кнопку Send me instructions!, я по ней кликнула, и получила ошибку. Пошла снизу по стеку ошибки, чтобы понять, с какой стати refused, увидела пустой email, записала нужное значение в таблицу, не заходя в админку, в целях экономии времени - и опять получила ошибку.  Ок, дождалась окончания урока, прочитала последний абзац и решила вернуть, как было. А нет - пустое значение нарушает ограничение Not NULL. Интересно, как у нас получилось создать юзера без адреса? Посмотрим, что с этим будет на следующем шаге. Может где-нибудь  в разделе вставить предупреждение  "Не трогать базу!"?

@Ольга_Миронова, поле email является опциональным в модели User: https://docs.djangoproject.com/en/5.0/ref/contrib/auth/#django.contrib.auth.models.User.email

@Дмитрий_Селезнев, Спасибо! Как-то не пришло сразу в голову, что (blank=true) != (null=true). Записала 1 пробел вместо адреса.

почему такой путь templates/registration/password_reset_form.html, если в джанго предопределен базовый путь 

accounts/password_reset/ [name='password_reset']

@Шамбер_Егор, потому что name='password_reset' это имя маршрута accounts/password_reset/, а templates/registration/password_reset_form.html это путь до шаблона, который определён по умолчанию как registration/password_reset_form.html для представления PasswordResetView: https://docs.djangoproject.com/en/5.0/topics/auth/default/#django.contrib.auth.views.PasswordResetView.template_name

Изменен Дмитрий Селезнев

@Шамбер_Егор, вот содержимое django.contrib.auth.urls:

from django.contrib.auth import views
from django.urls import path

urlpatterns = [
    path("login/", views.LoginView.as_view(), name="login"),
    path("logout/", views.LogoutView.as_view(), name="logout"),
    path(
        "password_change/", views.PasswordChangeView.as_view(), name="password_change"
    ),
    path(
        "password_change/done/",
        views.PasswordChangeDoneView.as_view(),
        name="password_change_done",
    ),
    path("password_reset/", views.PasswordResetView.as_view(), name="password_reset"),
    path(
        "password_reset/done/",
        views.PasswordResetDoneView.as_view(),
        name="password_reset_done",
    ),
    path(
        "reset/<uidb64>/<token>/",
        views.PasswordResetConfirmView.as_view(),
        name="password_reset_confirm",
    ),
    path(
        "reset/done/",
        views.PasswordResetCompleteView.as_view(),
        name="password_reset_complete",
    ),
]

После отправки вы будете перенаправлены на нашу последнюю страницу по умолчанию, предназначенную для завершения сброса пароля password_reset_complete.html

А как оно вообще работает если у нас нигде редиректы не указаны или это все "под капотом" прописано?

@User_678998186, Да, добавляя в маршруты path("accounts/", include("django.contrib.auth.urls")), мы грубо говоря добавляем в маршруты следующие адреса:

accounts/login/ [name='login']
accounts/logout/ [name='logout']
accounts/password_change/ [name='password_change']
accounts/password_change/done/ [name='password_change_done']
accounts/password_reset/ [name='password_reset']
accounts/password_reset/done/ [name='password_reset_done']
accounts/reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/reset/done/ [name='password_reset_complete']

 По которым отрабатываются встроенные функции авторизации в Django. А изменяя шаблоны:

password_reset_done.html
password_reset_confirm.html
password_reset_complete.html

Мы как бы переопределяем их, чтобы при вызове отрабатывали наши шаблоны, а не встроенные в Django.