Django База [2023]: Регистрация и авторизация пользователя #20
В этой статье мы рассмотрим создание формы регистрации, формы авторизации и функции деавторизации на Django, а также создадим необходимые представления для них.
Если вы хотите выразить благодарность автору сайта, статей и курса по Django, вы можете сделать это по ссылке ниже:
Создаем форму регистрации
В нашем приложении system, которое мы создавали в уроке с профилями откроем файл forms.py и в самый низ будем добавлять новые формы: регистрации и авторизации.
Первый фрагмент кода - форма регистрации.
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class UserRegisterForm(UserCreationForm):
"""
Переопределенная форма регистрации пользователей
"""
class Meta(UserCreationForm.Meta):
fields = UserCreationForm.Meta.fields + ('email', 'first_name', 'last_name')
def clean_email(self):
"""
Проверка email на уникальность
"""
email = self.cleaned_data.get('email')
username = self.cleaned_data.get('username')
if email and User.objects.filter(email=email).exclude(username=username).exists():
raise forms.ValidationError('Такой email уже используется в системе')
return email
def __init__(self, *args, **kwargs):
"""
Обновление стилей формы регистрации
"""
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields['username'].widget.attrs.update({"placeholder": 'Придумайте свой логин'})
self.fields['email'].widget.attrs.update({"placeholder": 'Введите свой email'})
self.fields['first_name'].widget.attrs.update({"placeholder": 'Ваше имя'})
self.fields["last_name"].widget.attrs.update({"placeholder": 'Ваша фамилия'})
self.fields['password1'].widget.attrs.update({"placeholder": 'Придумайте свой пароль'})
self.fields['password2'].widget.attrs.update({"placeholder": 'Повторите придуманный пароль'})
self.fields[field].widget.attrs.update({"class": "form-control", "autocomplete": "off"})
Так как встроенная система авторизации и регистрации нас устраивает, то можно воспользоваться переопределением форм. К переопределенной форме мы добавили метод проверки clean_email
на уникальность email адреса, также добавили поля для ввода при регистрации: email
, first_name
, last_name
. Стили используем для bootstrap.
Создаем форму авторизации
Отлично, теперь ниже создадим форму для авторизации. Код по-отдельности, поэтому после этих двух форм я скину полный код нашего forms.py, который мы создавали на основе предыдущих уроков.
from django.contrib.auth.forms import AuthenticationForm
class UserLoginForm(AuthenticationForm):
"""
Форма авторизации на сайте
"""
def __init__(self, *args, **kwargs):
"""
Обновление стилей формы регистрации
"""
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields['username'].widget.attrs['placeholder'] = 'Логин пользователя'
self.fields['password'].widget.attrs['placeholder'] = 'Пароль пользователя'
self.fields['username'].label = 'Логин'
self.fields[field].widget.attrs.update({
'class': 'form-control',
'autocomplete': 'off'
})
В примере выше мы наследуемся от существующей формы авторизации, добавив к ней стили от boostrap, и некоторые placeholder.
Полный файл forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from .models import Profile
class UserUpdateForm(forms.ModelForm):
"""
Форма обновления данных пользователя
"""
class Meta:
model = User
fields = ('username', 'email', 'first_name', 'last_name')
def __init__(self, *args, **kwargs):
"""
Обновление стилей формы под bootstrap
"""
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs.update({
'class': 'form-control',
'autocomplete': 'off'
})
def clean_email(self):
"""
Проверка email на уникальность
"""
email = self.cleaned_data.get('email')
username = self.cleaned_data.get('username')
if email and User.objects.filter(email=email).exclude(username=username).exists():
raise forms.ValidationError('Email адрес должен быть уникальным')
return email
class ProfileUpdateForm(forms.ModelForm):
"""
Форма обновления данных профиля пользователя
"""
class Meta:
model = Profile
fields = ('slug', 'birth_date', 'bio', 'avatar')
def __init__(self, *args, **kwargs):
"""
Обновление стилей формы обновления
"""
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs.update({
'class': 'form-control',
'autocomplete': 'off'
})
class UserRegisterForm(UserCreationForm):
"""
Переопределенная форма регистрации пользователей
"""
class Meta(UserCreationForm.Meta):
fields = UserCreationForm.Meta.fields + ('email', 'first_name', 'last_name')
def clean_email(self):
"""
Проверка email на уникальность
"""
email = self.cleaned_data.get('email')
username = self.cleaned_data.get('username')
if email and User.objects.filter(email=email).exclude(username=username).exists():
raise forms.ValidationError('Такой email уже используется в системе')
return email
def __init__(self, *args, **kwargs):
"""
Обновление стилей формы регистрации
"""
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields['username'].widget.attrs.update({"placeholder": 'Придумайте свой логин'})
self.fields['email'].widget.attrs.update({"placeholder": 'Введите свой email'})
self.fields['first_name'].widget.attrs.update({"placeholder": 'Ваше имя'})
self.fields["last_name"].widget.attrs.update({"placeholder": 'Ваша фамилия'})
self.fields['password1'].widget.attrs.update({"placeholder": 'Придумайте свой пароль'})
self.fields['password2'].widget.attrs.update({"placeholder": 'Повторите придуманный пароль'})
self.fields[field].widget.attrs.update({"class": "form-control", "autocomplete": "off"})
class UserLoginForm(AuthenticationForm):
"""
Форма авторизации на сайте
"""
def __init__(self, *args, **kwargs):
"""
Обновление стилей формы регистрации
"""
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields['username'].widget.attrs['placeholder'] = 'Логин пользователя'
self.fields['password'].widget.attrs['placeholder'] = 'Пароль пользователя'
self.fields['username'].label = 'Логин'
self.fields[field].widget.attrs.update({
'class': 'form-control',
'autocomplete': 'off'
})
Представление для регистрации пользователя
Переходим к представлениям, в файле views.py нашего приложения system добавим следующий код для представления регистрации пользователя:
from django.views.generic import CreateView
from django.urls import reverse_lazy
from django.contrib.messages.views import SuccessMessageMixin
from .forms import UserRegisterForm
class UserRegisterView(SuccessMessageMixin, CreateView):
"""
Представление регистрации на сайте с формой регистрации
"""
form_class = UserRegisterForm
success_url = reverse_lazy('home')
template_name = 'system/user_register.html'
success_message = 'Вы успешно зарегистрировались. Можете войти на сайт!'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Регистрация на сайте'
return context
В примере выше мы используем миксин (урок с миксинами ) и представление CreateView (урок с CRUD запросами), также импортируем нашу созданную форму из файла forms.py.
Представление для авторизации пользователя
Теперь создаем представление для авторизации, также во views.py:
from django.contrib.messages.views import SuccessMessageMixin
from django.contrib.auth.views import LoginView
from .forms UserLoginForm
class UserLoginView(SuccessMessageMixin, LoginView):
"""
Авторизация на сайте
"""
form_class = UserLoginForm
template_name = 'system/user_login.html'
next_page = 'home'
success_message = 'Добро пожаловать на сайт!'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Авторизация на сайте'
return context
В данном примере мы также используем миксин, но наследуемся уже от готового класса представления LoginView.
Представление для деавторизации пользователя
Это самая мелочь, но необходимая часть каждого проекта с системой авторизации. Также в файл views.py после других представлений добавим следующий фрагмент кода:
from django.contrib.auth.views import LogoutView
class UserLogoutView(LogoutView):
"""
Выход с сайта
"""
next_page = 'home'
В примере выше мы наследуемся от встроенного представлений LogoutView и переправляем пользователя на домашнюю страницу.
Полный файл views.py на основе всех предыдущих уроков
И весь файл views.py основанный на предыдущих уроках, включаяя изменения из этого урока:
from django.views.generic import DetailView, UpdateView, CreateView
from django.db import transaction
from django.urls import reverse_lazy
from django.contrib.messages.views import SuccessMessageMixin
from django.contrib.auth.views import LoginView, LogoutView
from .models import Profile
from .forms import UserUpdateForm, ProfileUpdateForm, UserRegisterForm, UserLoginForm
class ProfileDetailView(DetailView):
"""
Представление для просмотра профиля
"""
model = Profile
context_object_name = 'profile'
template_name = 'system/profile_detail.html'
queryset = model.objects.all().select_related('user')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = f'Страница пользователя: {self.object.user.username}'
return context
class ProfileUpdateView(UpdateView):
"""
Представление для редактирования профиля
"""
model = Profile
form_class = ProfileUpdateForm
template_name = 'system/profile_edit.html'
def get_object(self, queryset=None):
return self.request.user.profile
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = f'Редактирование профиля пользователя: {self.request.user.username}'
if self.request.POST:
context['user_form'] = UserUpdateForm(self.request.POST, instance=self.request.user)
else:
context['user_form'] = UserUpdateForm(instance=self.request.user)
return context
def form_valid(self, form):
context = self.get_context_data()
user_form = context['user_form']
with transaction.atomic():
if all([form.is_valid(), user_form.is_valid()]):
user_form.save()
form.save()
else:
context.update({'user_form': user_form})
return self.render_to_response(context)
return super(ProfileUpdateView, self).form_valid(form)
def get_success_url(self):
return reverse_lazy('profile_detail', kwargs={'slug': self.object.slug})
class UserRegisterView(SuccessMessageMixin, CreateView):
"""
Представление регистрации на сайте с формой регистрации
"""
form_class = UserRegisterForm
success_url = reverse_lazy('home')
template_name = 'system/user_register.html'
success_message = 'Вы успешно зарегистрировались. Можете войти на сайт!'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Регистрация на сайте'
return context
class UserLoginView(SuccessMessageMixin, LoginView):
"""
Авторизация на сайте
"""
form_class = UserLoginForm
template_name = 'system/user_login.html'
next_page = 'home'
success_message = 'Добро пожаловать на сайт!'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Авторизация на сайте'
return context
class UserLogoutView(LogoutView):
"""
Выход с сайта
"""
next_page = 'home'
Настройка urls.py для представлений
Теперь нам необходимо обработать наши представления, для этого в файле urls.py нашего приложения system добавим следующие строки:
from django.urls import path
from .views import ProfileUpdateView, ProfileDetailView, UserRegisterView, UserLoginView, UserLogoutView
urlpatterns = [
path('user/edit/', ProfileUpdateView.as_view(), name='profile_edit'),
path('user/<str:slug>/', ProfileDetailView.as_view(), name='profile_detail'),
path('register/', UserRegisterView.as_view(), name='register'),
path('login/', UserLoginView.as_view(), name='login'),
path('logout/', UserLogoutView.as_view(), name='logout'),
]
Осталось лишь одно, создать шаблоны и проверить в деле.
Создание шаблонов для регистрации и авторизации
В папке templates/system создаем два файла: user_register.html и user_login.html
В файл user_register.html добавляем следующую HTML разметку:
{% extends 'main.html' %}
{% block content %}
<div class="card mb-3 border-0 nth-shadow">
<div class="card-body">
<div class="card-title nth-card-title">
<h4>Регистрация на сайте</h4>
</div>
<form method="post" action="{% url 'register' %}" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-dark mt-2">Зарегистрироваться</button>
</form>
</div>
</div>
{% endblock %}
А в файл user_login.html следующий фрагмент:
{% extends 'main.html' %}
{% block content %}
<div class="card mb-3 border-0 nth-shadow">
<div class="card-body">
<div class="card-title nth-card-title">
<h4>Авторизация на сайте</h4>
</div>
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
{{ form.as_p }}
<div class="d-grid gap-2 d-md-block mt-2">
<button type="submit" class="btn btn-dark">Авторизоваться</button>
</div>
</form>
</div>
</div>
{% endblock %}
Для деавторизации мы не создаем никаких шаблонов.
Проверяем результаты работы форм и представлений
Теперь проверим, как все у нас работает. Для этого я разлогинюсь из аккаунта и попробую зарегистрировать новый аккаунт и авторизоваться с помощью него.
Отлично, у нас получилось все, как нужно. Надеюсь и у вас все получилось!