К этому моменту в проекте cinemahub мы уже научились:
- авторизовывать пользователей;
- ограничивать доступ к страницам;
- связывать фильмы с их авторами.
Однако если пользователь не может создать аккаунт, весь этот функционал теряет практический смысл. Да, Django предоставляет готовую модель пользователя и даже встроенные представления, но в реальных проектах почти всегда требуется собственная логика регистрации:
- дополнительные поля;
- проверка email;
- контроль паролей;
- кастомные шаблоны.
В этом уроке мы реализуем полный цикл регистрации пользователя через функцию представления, чтобы глубже понять, как работает механизм аутентификации Django.
Процесс регистрации в Django почти всегда состоит из четырёх шагов:
-
Пользователь открывает страницу регистрации
-
Django отображает HTML-форму
-
Пользователь отправляет данные
-
Сервер:
- валидирует данные;
- создаёт пользователя;
- хэширует пароль;
- сохраняет пользователя в базе
Мы реализуем каждый шаг вручную, не скрывая детали.
Начнём с шаблона. Он почти идентичен форме входа, но это нормально — пользователю важна визуальная целостность интерфейса.
Файл: users/templates/users/register.html
{% extends 'base.html' %} {% block content %}
<h1>Регистрация в Cinemahub</h1>
<form method="post">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}" />
{{ form.as_p }}
<p>
<button type="submit">Зарегистрироваться</button>
</p>
</form>
{% endblock %}Что здесь важно:
{% csrf_token %}— обязательная защита от CSRF-атак;form.as_p— быстрый способ вывести поля формы;- скрытое поле
next— задел на будущее (после регистрации можно делать редирект).
Теперь опишем форму, которая будет:
- создавать пользователя;
- проверять пароли;
- контролировать email.
Файл: users/forms.py
from django import forms
from django.contrib.auth import get_user_model
User = get_user_model()
class RegisterUserForm(forms.ModelForm):
username = forms.CharField(label="Логин")
password = forms.CharField(
label="Пароль",
widget=forms.PasswordInput
)
password2 = forms.CharField(
label="Повтор пароля",
widget=forms.PasswordInput
)
class Meta:
model = User
fields = [
'username',
'email',
'first_name',
'last_name',
'password',
'password2'
]
labels = {
'email': 'E-mail',
'first_name': 'Имя',
'last_name': 'Фамилия',
}Это принципиально важный момент:
- сегодня используется стандартный
User; - завтра — кастомная модель пользователя;
- код не придётся переписывать.
Если этого не сделать, пользователь может зарегистрироваться с опечаткой в пароле.
Добавим кастомную валидацию.
Файл: users/forms.py
def clean_password2(self):
cd = self.cleaned_data
if cd['password'] != cd['password2']:
raise forms.ValidationError("Пароли не совпадают")
return cd['password2']По умолчанию Django не запрещает повторяющиеся email.
Добавим эту проверку вручную.
def clean_email(self):
email = self.cleaned_data['email']
if User.objects.filter(email=email).exists():
raise forms.ValidationError("Пользователь с таким E-mail уже существует")
return emailТеперь объединим форму и шаблон в полноценную логику.
Файл: users/views.py
from django.shortcuts import render
from django.contrib.auth import login
from .forms import RegisterUserForm
def register(request):
if request.method == 'POST':
form = RegisterUserForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.set_password(form.cleaned_data['password'])
user.save()
return render(request, 'users/register_done.html')
else:
form = RegisterUserForm()
return render(request, 'users/register.html', {'form': form})-
При
GET:- создаётся пустая форма;
- отображается шаблон.
-
При
POST:- данные валидируются;
- пользователь создаётся без сохранения;
- пароль хэшируется через
set_password(); - пользователь сохраняется в базе.
❌ Нельзя:
user.password = form.cleaned_data['password']✔ Нужно:
user.set_password(...)Причина:
- Django хранит хэши, а не пароли;
- без
set_password()пользователь не сможет войти.
Файл: users/templates/users/register_done.html
{% extends 'base.html' %} {% block content %}
<h1>Регистрация завершена</h1>
<p>
Вы успешно зарегистрировались на Cinemahub. Теперь вы можете
<a href="{% url 'users:login' %}">войти в систему</a>.
</p>
{% endblock %}Файл: users/urls.py
from django.urls import path
from . import views
from django.contrib.auth.views import LogoutView
app_name = 'users'
urlpatterns = [
path('login/', views.LoginUser.as_view(), name='login'),
path('logout/', LogoutView.as_view(), name='logout'),
path('register/', views.register, name='register'),
]Файл: templates/base.html
<header>
{% if request.user.is_authenticated %}
<p>Привет, {{ request.user.username }}!</p>
<a href="{% url 'users:logout' %}">Выйти</a>
{% else %}
<a href="{% url 'users:login' %}">Войти</a> |
<a href="{% url 'users:register' %}">Регистрация</a>
{% endif %}
<h1><a href="{% url 'movies:index' %}">Кино-проекты</a></h1>
<nav>
<!-- Общие блоки тегов/жанров/режиссёров можно сюда включать -->
</nav>
</header>-
Запустите сервер
-
Перейдите по ссылке «Регистрация»
-
Попробуйте:
- разные пароли;
- одинаковые email;
- корректную регистрацию
-
Проверьте:
- пользователь появился в админке;
- пароль не отображается в явном виде;
- вход работает корректно.
Ошибка: пользователь создаётся, но не может войти
Причина: не использован set_password()
Ошибка: форма всегда невалидна
Причина: забыли password2 в fields
Ошибка: email дублируется
Причина: нет clean_email()
Добавьте поле email обязательным для заполнения.
Сделайте автоматический вход пользователя сразу после регистрации.
Добавьте вывод имени пользователя в шаблон успешной регистрации.
email = forms.EmailField(label="E-mail", required=True)from django.contrib.auth import login
login(request, user)<p>Добро пожаловать, {{ user.username }}!</p>(передать user в контекст)
- Зачем использовать
get_user_model()? - Почему нельзя сохранять пароль напрямую?
- Где выполняется валидация формы?
- Чем отличается
GETотPOSTв представлении? - Зачем нужен
commit=False? - Где лучше проверять совпадение паролей?
- Что произойдёт без
csrf_token? - Как проверить регистрацию без тестов?