Классы LoginView, LogoutView и AuthenticationForm

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

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

На прошлом занятии мы с вами вручную реализовали механизм авторизации пользователей на сайте. На этом перепишем его с использованием стандартных классов, поставляемых фреймворком Django. Они следующие:

  • LoginView – класс представления для авторизации пользователей;
  • LogoutView – класс представления для выхода пользователя из системы;
  • AuthenticationForm – класс формы обработки аутентификации пользователя.

Начнем с класса LoginView, которым заменим функцию login_user() следующим образом:

class LoginUser(LoginView):
    form_class = AuthenticationForm
    template_name = 'users/login.html'
    extra_context = {'title': "Авторизация"}

Вам здесь должны быть знакомы все используемые атрибуты. И, обратите внимание, атрибуту form_class мы присваиваем стандартный класс формы AuthenticationForm фреймворка Django. Если здесь прописать наш текущий класс формы:

form_class = LoginUserForm

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

form_class = AuthenticationForm

Либо, при необходимости, расширить ее дочерним классом. Пока оставим наше представление в таком виде. Подключим его к маршруту login в файле users/urls.py:

urlpatterns = [
    path('login/', views.LoginUser.as_view(), name='login'),
    path('logout/', views.logout_user, name='logout'),
]

Запустим веб-сервер и перейдем в форму авторизации. Как видите, все отображается и при вводе логина и пароля происходит автоматическое перенаправление в профайл по URL-адресу:

http://127.0.0.1:8000/accounts/profile/

Но нам это не нужно. Изменим адрес перенаправления, переопределив метод get_success_url() в классе LoginUser:

    def get_success_url(self):
        return reverse_lazy('home')

Теперь, при авторизации будем попадать на главную страницу. Тот же самый эффект можно получить, определив константу:

LOGIN_REDIRECT_URL = '/'

в файле settings.py пакета конфигурации sitewomen. Но лучше здесь указывать имена маршрутов:

LOGIN_REDIRECT_URL = 'home'

Вообще, в файле конфигурации settings.py можно прописывать следующие параметры:

  • LOGIN_REDIRECT_URL – задает URL-адрес, на который следует перенаправлять пользователя после успешной авторизации;
  • LOGIN_URL – определяет URL-адрес, на который следует перенаправить неавторизованного пользователя при попытке посетить закрытую страницу сайта;
  • LOGOUT_REDIRECT_URL – задает URL-адрес, на который перенаправляется пользователь после выхода.

Улучшение формы авторизации

Как видите, класс представления LoginView берет на себя значимую часть стандартной работы по авторизации пользователя. И дополнительно он формирует сообщения об ошибках, которые передает в форму, при неверно заполненных данных. Давайте в этом убедимся. Для этого откроем шаблон формы login.html и вывод полей формы будем делать через цикл:

<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 %}

Обратите внимание, вначале не забываем делать отображение общих ошибок коллекции form.non_field_errors.

Обновим страницу авторизации и введем неверную пару логин/пароль. Увидим отображение ошибки. Эта ошибка, была автоматически сформирована классом представления LoginView. И это очень удобно. Обработку всех стандартных ситуаций фреймворк Django берет на себя.

Но мы бы хотели еще улучшить и сам класс формы. Для этого в файле users/forms.py существующий класс LoginUserForm унаследуем от класса AuthenticationForm следующим образом:

class LoginUserForm(AuthenticationForm):
    username = forms.CharField(label='Логин', widget=forms.TextInput(attrs={'class': 'form-input'}))
    password = forms.CharField(label='Пароль', widget=forms.PasswordInput(attrs={'class': 'form-input'}))

Обновим форму авторизации и видим, что в целом все работает. Обратите внимание, что в этой форме поля должны называться именно так, как прописаны: username и password. Вообще, они должны соответствовать стандартной модели пользователя User фреймворка Django. Поэтому, как вариант, мы могли бы определить этот же класс LoginUserForm следующим образом:

class LoginUserForm(AuthenticationForm):
    class Meta:
        model = get_user_model()
        fields = ['username', 'password']

Мы здесь получаем модель User с помощью стандартной функции get_user_model(). Это рекомендуемая практика на случай изменения модели. Тогда в программе ничего дополнительно менять не придется. Также указали отображать в форме поля username и password. Именно они необходимы для аутентификации пользователя по БД.

В общем случае, можно комбинировать мета-описание и отдельные атрибуты. Что я и сделаю:

class LoginUserForm(AuthenticationForm):
    username = forms.CharField(label='Логин', widget=forms.TextInput(attrs={'class': 'form-input'}))
    password = forms.CharField(label='Пароль', widget=forms.PasswordInput(attrs={'class': 'form-input'}))
 
    class Meta:
        model = User
        fields = ['username', 'password']

Так форма выглядит понятнее по своей логике работы.

Параметр next

Последний штрих, который мы сделаем в форме – это добавим скрытое поле с именем next в шаблон login.html следующим образом:

<input type="hidden" name="next" value="{{ next }}" />

Что это за параметр? Дело в том, что теперь мы можем в URL-адресе формы авторизации дополнительно прописывать этот параметр next, например, так:

http://127.0.0.1:8000/users/login/?next=/addpage/

Он содержит URL-адрес страницы, на которую следует сделать перенаправление после успешной авторизации. В данном случае, мы попадаем на страницу добавления статей. Причем, этот параметр next имеет более высокий приоритет, чем параметр LOGIN_REDIRECT_URL, прописанный в пакете конфигурации.

Зачем вообще может понадобиться этот параметр? Возможно, вы замечали, как выбираете товар в интернет-магазине и для его помещения в корзину необходима авторизация. Форма ввода логина/пароля сразу появляется на экране и после корректного ввода, вы сразу попадаете в корзину. Как вы думаете, как сайт догадался вас перенаправить именно в корзину? Да, сработал аналог параметра next для перенаправления на нужную страницу сайта. И то же самое могло произойти, когда товар уже в корзине, а для оформления заказа понадобилась авторизация. Тогда посетитель перенаправляется уже не в корзину, а на оформление заказа. То есть, иногда нам нужна не какая-то определенная страница после авторизации, а произвольная, адрес которой можно сохранить в параметре next. Это очень удобно и в фреймворк Django этот функционал уже встроен.

Класс LogoutView

Последний класс, который мы рассмотрим на этом занятии – это LogoutView. С его помощью можно заменить функцию представления logout_user(). Если нам достаточно стандартного поведения, то класс LogoutView можно сразу связать с нужным маршрутом (в файле users/urls.py):

urlpatterns = [
    path('login/', views.LoginUser.as_view(), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
]

А функцию logout_user() просто убрать. Переходим на сайт, нажимаем «Выйти» и попадаем на несуществующую страницу:

http://127.0.0.1:8000/users/logout/

Как нам задать другой адрес для выхода? Для этого можно воспользоваться константой LOGOUT_REDIRECT_URL, прописав ее в файле settings.py, например, так:

LOGOUT_REDIRECT_URL = 'home'

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

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

Видео по теме