Доработки системы авторизации и регистрации, добавление сессий
Регистрация
Встроенной view регистрации в Django нет, но есть форма UserCreationForm (имя, пароль и его повтор с проверкой валидаторами). Расширим её полем email:
# account/forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class UserRegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta(UserCreationForm.Meta):
model = User
fields = ["username", "first_name", "email"]
def clean_email(self):
email = self.cleaned_data["email"].lower()
if User.objects.filter(email__iexact=email).exists():
raise forms.ValidationError("Пользователь с таким email уже зарегистрирован.")
return email
View:
# account/views.py
from django.contrib import messages
from django.contrib.auth import login
from django.shortcuts import redirect, render
from .forms import UserRegistrationForm
def register(request):
if request.method == "POST":
form = UserRegistrationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
messages.success(request, "Добро пожаловать! Регистрация прошла успешно.")
return redirect("dashboard")
else:
form = UserRegistrationForm()
return render(request, "account/register.html", {"form": form})
path("register/", views.register, name="register").
Регистрация на этом учебном сайте работает именно так.
Вход по email
По умолчанию вход — по username. Чтобы разрешить вход по email, напишем бэкенд аутентификации:
# account/authentication.py
from django.contrib.auth.models import User
class EmailAuthBackend:
def authenticate(self, request, username=None, password=None):
try:
user = User.objects.get(email__iexact=username)
except (User.DoesNotExist, User.MultipleObjectsReturned):
return None
if user.check_password(password) and user.is_active:
return user
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
# settings.py
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
"account.authentication.EmailAuthBackend",
]
Django перебирает бэкенды по очереди, пока один из них не вернёт пользователя. Теперь в поле «Имя пользователя» можно ввести и логин, и email.
Сообщения
Фреймворк messages показывает одноразовые уведомления («Профиль сохранён»):
messages.success(request, "Профиль обновлён")
messages.error(request, "Не удалось сохранить")
messages.warning(request, "...")
messages.info(request, "...")
В base.html:
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li class="{{ message.tags }}">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
Сообщение показывается один раз — после вывода оно удаляется. Сообщения хранятся в сессии или cookie, поэтому переживают редирект.
Сессии
HTTP не хранит состояние: каждый запрос — сам по себе. Как же сайт «помнит», что вы вошли? С помощью сессий.
- При первом обращении Django создаёт сессию и отправляет браузеру cookie
sessionidсо случайным ключом. - Браузер присылает эту cookie с каждым запросом.
- По ключу Django находит данные сессии (по умолчанию — в таблице
django_session).
login() записывает в сессию id пользователя — так request.user узнаёт вас на следующих страницах.
Работа с сессией
request.session ведёт себя как словарь:
request.session["theme"] = "dark" # сохранить
theme = request.session.get("theme", "light") # прочитать
del request.session["theme"] # удалить
"theme" in request.session # проверить
request.session.flush() # очистить сессию полностью
Пример — последние просмотренные посты:
def post_detail(request, ...):
...
viewed = request.session.get("viewed_posts", [])
if post.id not in viewed:
viewed = [post.id] + viewed[:4]
request.session["viewed_posts"] = viewed
Изменение вложенных объектов
Django сохраняет сессию, когда вы присваиваете ключ. Если вы меняете список или словарь «на месте» (request.session["cart"].append(5)), установите request.session.modified = True.
Настройки сессий
| Настройка | Значение |
|---|---|
SESSION_ENGINE |
Где хранить: db (по умолчанию), cache, cached_db, file, signed_cookies |
SESSION_COOKIE_AGE |
Время жизни в секундах (по умолчанию 2 недели) |
SESSION_EXPIRE_AT_BROWSER_CLOSE |
Завершать при закрытии браузера |
SESSION_COOKIE_SECURE |
Отправлять cookie только по HTTPS (включите в продакшене) |
Функция «Запомнить меня»:
if not form.cleaned_data.get("remember_me"):
request.session.set_expiry(0) # до закрытия браузера
Храните в сессии только небольшие данные, которые можно сериализовать в JSON (числа, строки, списки, словари), — не объекты моделей.
Итог
- Регистрация:
UserCreationForm+ своя view +login()после сохранения. - Свой бэкенд аутентификации позволяет входить по email.
messages— одноразовые уведомления, переживающие редирект.- Сессия — словарь
request.session, привязанный к посетителю через cookie.
Практика решено 0 из 6
Создайте форму регистрации UserRegistrationForm на основе UserCreationForm,
добавив в поля модели User поле email (в Meta унаследуйте UserCreationForm.Meta).
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class UserRegistrationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = User
fields = ["username", "email"]
Напишите view register: на POST проверяет форму UserRegistrationForm, сохраняет пользователя,
сразу выполняет вход (login(request, user)) и перенаправляет на dashboard.
На GET — пустая форма. Шаблон — account/register.html.
from django.contrib.auth import login
def register(request):
if request.method == "POST":
form = UserRegistrationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
return redirect("dashboard")
else:
form = UserRegistrationForm()
return render(request, "account/register.html", {"form": form})
Где по умолчанию Django хранит данные сессии?
Реализуйте счётчик просмотров страницы для текущего посетителя:
возьмите из сессии значение views (по умолчанию 0), увеличьте на 1 и сохраните обратно в сессию.
def counter(request):
request.session["views"] = request.session.get("views", 0) + 1
return HttpResponse(f"Вы открыли эту страницу {request.session['views']} раз")
Вы изменили список в сессии: request.session["cart"].append(5). Почему изменение может не сохраниться?
Какие уровни сообщений есть во фреймворке django.contrib.messages?
Войдите, чтобы отмечать пройденные уроки.