В предыдущем уроке мы реализовали авторизацию пользователей с помощью функционального представления. Такой подход полезен для понимания внутренней логики Django, но на практике используется не всегда.
Django предоставляет готовые классы представлений, которые решают ту же задачу:
- обрабатывают форму входа;
- проверяют учетные данные;
- создают и удаляют сессии;
- обрабатывают ошибки.
В этом уроке мы научимся использовать эти классы, адаптировать их под проект cinemahub и понимать, когда они предпочтительнее ручной реализации.
Функциональные представления дают гибкость, но имеют и недостатки:
- больше кода;
- выше вероятность ошибок;
- сложнее поддерживать проект.
Классы Django решают эти проблемы:
- логика авторизации уже реализована и протестирована;
- код становится короче и чище;
- поведение легко настраивается через переопределение атрибутов и методов.
В этом уроке мы разберём три ключевых компонента:
LoginView— отвечает за вход пользователя;LogoutView— выполняет выход из системы;AuthenticationForm— стандартная форма аутентификации.
Начнём с замены функции login_user() на класс-представление.
from django.contrib.auth.views import LoginView
from django.contrib.auth.forms import AuthenticationForm
class LoginUser(LoginView):
form_class = AuthenticationForm
template_name = "users/login.html"
extra_context = {
"title": "Авторизация в Cinemahub"
}-
LoginViewуже содержит всю логику:- проверку данных;
- вызов
authenticate()иlogin(); - обработку ошибок.
-
form_class— указываем, какую форму использовать. -
template_name— путь к HTML-шаблону. -
extra_context— дополнительные данные, доступные в шаблоне.
Мы не пишем логику входа вручную, а используем готовый механизм Django.
Теперь заменим маршрут входа на класс-представление.
from django.urls import path
from .views import LoginUser
app_name = "users"
urlpatterns = [
path("login/", LoginUser.as_view(), name="login"),
]-
Запустите сервер:
python manage.py runserver
-
Перейдите по адресу:
/users/login/ -
Введите корректные данные пользователя.
Если авторизация проходит успешно — значит LoginView работает корректно.
Если ничего не настраивать, Django попытается перенаправить пользователя на:
/accounts/profile/
Такого URL в проекте cinemahub нет, поэтому это приведёт к ошибке 404.
from django.urls import reverse_lazy
class LoginUser(LoginView):
form_class = AuthenticationForm
template_name = "users/login.html"
def get_success_url(self):
return reverse_lazy("movies:movie_list")Теперь после входа пользователь попадает на страницу со списком фильмов.
LOGIN_REDIRECT_URL = "movies:movie_list"Этот способ удобен, если логика одинакова для всего проекта.
В settings.py также часто используют:
LOGIN_URL = "users:login"
LOGOUT_REDIRECT_URL = "movies:movie_list"LOGIN_URL— куда перенаправлять неавторизованных пользователей;LOGOUT_REDIRECT_URL— куда перенаправлять после выхода.
AuthenticationForm — это стандартная форма Django, которая:
- проверяет логин и пароль;
- обрабатывает ошибки;
- передаёт сообщения об ошибках в шаблон.
<h1>{{ title }}</h1>
<form method="post">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="form-error">
{{ form.non_field_errors }}
</div>
{% endif %}
{% for field in form %}
<p>
{{ field.label_tag }}<br>
{{ field }}
{{ field.errors }}
</p>
{% endfor %}
<button type="submit">Войти</button>
</form>Теперь ошибки входа будут отображаться пользователю корректно.
Иногда нужно изменить внешний вид формы, не переписывая логику.
from django.contrib.auth.forms import AuthenticationForm
from django import forms
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"})
)И подключаем её в LoginView:
class LoginUser(LoginView):
form_class = LoginUserForm
template_name = "users/login.html"Django автоматически поддерживает параметр next.
Пример URL:
/users/login/?next=/movies/add/
После успешного входа пользователь будет перенаправлён на /movies/add/.
Чтобы это работало, добавьте в шаблон:
<input type="hidden" name="next" value="{{ next }}">Теперь заменим функцию выхода стандартным классом.
from django.contrib.auth.views import LogoutView
urlpatterns = [
path("logout/", LogoutView.as_view(), name="logout"),
]LogoutView:
- удаляет данные сессии;
- делает пользователя анонимным;
- перенаправляет на
LOGOUT_REDIRECT_URL.
-
Авторизуйтесь.
-
Перейдите по адресу:
/users/logout/ -
Проверьте, что:
- пользователь вышел из системы;
- повторный вход в защищённые страницы требует авторизации.
<header>
{% if request.user.is_authenticated %}
<p>Привет, {{ request.user.username }}!</p>
<form action="{% url 'users:logout' %}" method="post" style="display:inline;">
{% csrf_token %}
<button type="submit">Выйти</button>
</form>
{% else %}
<a href="{% url 'users:login' %}">Войти</a> |
{% endif %}
<h1><a href="{% url 'movies:index' %}">Кино-проекты</a></h1>
<nav>
<!-- Общие блоки тегов/жанров/режиссёров можно сюда включать -->
</nav>
</header>Причина — не настроен LOGIN_REDIRECT_URL или get_success_url().
Причины:
- неверный путь к шаблону;
- ошибка в
template_name.
Причина — отсутствует django.contrib.auth в INSTALLED_APPS.
Ошибка HTTP 405 (Method Not Allowed) возникает потому, что стандартный класс LogoutView из Django по умолчанию принимает только HTTP метод POST для выхода из профиля, а не GET-запрос.
Измените заголовок страницы входа на «Вход для администраторов Cinemahub».
Сделайте так, чтобы после выхода пользователь перенаправлялся на страницу списка фильмов (За это отвечает параметр в settings.py LOGOUT_REDIRECT_URL).
Переопределите get_success_url(), чтобы пользователь всегда попадал на первый опубликованный фильм.
extra_context = {"title": "Вход для администраторов Cinemahub"}LOGOUT_REDIRECT_URL = "movies:movie_list"from movies.models import Movie
def get_success_url(self):
movie = Movie.objects.filter(is_published=True).first()
return movie.get_absolute_url()- Чем
LoginViewлучше функционального представления? - Где Django хранит логику проверки пароля?
- Что делает
AuthenticationForm? - Зачем нужен параметр
next? - Как изменить URL после входа?
- Когда лучше использовать
LOGIN_REDIRECT_URL? - Что делает
LogoutView? - Можно ли использовать собственную форму с
LoginView? - Где обрабатываются ошибки авторизации?