Курс по Django: https://stepik.org/a/183363
Архив проекта: 66_sitewomen.zip
На этом занятии
мы с вами реализуем довольно распространенный способ авторизации по E-mail адресу и
паролю. Не зря на предыдущих занятиях при регистрации пользователей требовался
уникальный E-mail. Вообще, им
можно заменить логин и не просить пользователя вводить эти дополнительные
данные.
По умолчанию в Django используется бэкенд
ModelBackend для
аутентификации пользователя по паре логин (username) и пароль (password):
https://docs.djangoproject.com/en/4.2/topics/auth/customizing/
В настроечном
файле settings.py его можно явно
прописать, используя параметр AUTHENTICATION_BACKENDS:
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
]
Если перейти к
определению класса ModelBackend, то увидим, что он наследуется от
базового класса BaseBackend, в котором определены методы, необходимые для
работы механизма аутентификации. При этом главными, непосредственно для
аутентификации пользователя, являются методы:
- authenticate() –
непосредственно аутентификация по username и password; возвращается
объект пользователя, либо None, если он не был найден;
- get_user() –
получение объекта пользователя по идентификатору.
Чтобы создать
свой бэкенд аутентификации, как минимум, нужно определить в нем эти два метода.
И давайте это делаем. Для этого в приложении users создадим еще
один файл с именем authentication.py, в котором
определим новый класс бэкенда с именем EmailAuthBackend следующим
образом:
from django.contrib.auth.backends import BaseBackend
class EmailAuthBackend(BaseBackend):
...
И добавим в этот
класс метод authenticate(), сохранив его сигнатуру (наборы параметров),
следующим образом:
class EmailAuthBackend(BaseBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
user_model = get_user_model()
try:
user = user_model.objects.get(email=username)
if user.check_password(password):
return user
return None
except (user_model.DoesNotExist, user_model.MultipleObjectsReturned):
return None
Здесь
используется стандартное имя параметра username, чтобы наш
новый бэкенд согласованно работал с фреймворком Django. Но мы
подразумеваем, что сюда будет передаваться E-mail, по которому,
затем, выделяется запись из таблицы user. Если запись
найдена и пароль совпадает, то аутентификация прошла успешно и возвращается
объект пользователя. Иначе возвращаем None, а также в том
случае, если запись не была найдена или было получено несколько записей с
указанным E-mail.
По идее, мы уже
сейчас можем подключить наш бэкенд к Django и посмотреть,
как он будет работать. Для этого в файле конфигурации settings.py в список AUTHENTICATION_BACKENDS
следует добавить строчку:
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'users.authentication.EmailAuthBackend',
]
То есть, у нас
будут работать оба бэкенда: и по логину и по E-mail. Эти классы
просматриваются по порядку в списке и срабатывает первый вернувший объект
пользователя, остальные пропускаются. В результате, пользователи могут авторизоваться
и по логину и по E-mail.
Если сейчас
запустить тестовый веб-сервер и перейти в форму авторизации, то при вводе
корректного E-mail и пароля мы
перейдем на главную страницу. Это, как раз, результат работы метода authenticate().
Но, при этом, в главном меню не видим отображения имени пользователя. Это из-за
того, что мы не прописали второй метод get_user() в классе EmailAuthBackend:
def get_user(self, user_id):
user_model = get_user_model()
try:
return user_model.objects.get(pk=user_id)
except user_model.DoesNotExist:
return None
Теперь, при
вводе E-mail и пароля
пользователь будет авторизован и отображен на панели главного меню. Вот так в Django очень просто
можно создавать свои собственные бэкенды авторизации пользователей на сайте.
Создание профайла пользователя
Во второй части
этого занятия создадим страницу с профилем пользователя. Первым делом определим
шаблон в файле users/profile.html следующим
образом:
{% extends 'base.html' %}
{% block content %}
<h1>Профиль</h1>
<form method="post">
{% csrf_token %}
<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 %}
<p ><button type="submit">Сохранить</button></p>
</form>
{% endblock %}
И класс формы ProfileUserForm
для этого шаблона:
class ProfileUserForm(forms.ModelForm):
username = forms.CharField(disabled=True, label='Логин', widget=forms.TextInput(attrs={'class': 'form-input'}))
email = forms.CharField(disabled=True, label='E-mail', widget=forms.TextInput(attrs={'class': 'form-input'}))
class Meta:
model = get_user_model()
fields = ['username', 'email', 'first_name', 'last_name']
labels = {
'first_name': 'Имя',
'last_name': 'Фамилия',
}
widgets = {
'first_name': forms.TextInput(attrs={'class': 'form-input'}),
'last_name': forms.TextInput(attrs={'class': 'form-input'}),
}
Здесь все
достаточно просто. Обратите внимание, у полей username и email мы установили
параметр disabled=True, сделали их
неактивными и неизменяемыми. Все остальное можно редактировать.
Далее, в файле users/view.py объявим класс
представления для работы с этой формой:
class ProfileUser(LoginRequiredMixin, UpdateView):
model = get_user_model()
form_class = ProfileUserForm
template_name = 'users/profile.html'
extra_context = {'title': "Профиль пользователя"}
def get_success_url(self):
return reverse_lazy('users:profile', args=[self.request.user.pk])
Мы его наследуем
от класса LoginRequiredMixin, чтобы
запретить доступ неавторизованным пользователям. А класс UpdateView берет на себя
функционал по изменению данных в профиле пользователя. Метод get_success_url() необходим для
перенаправления на текущую страницу при изменении данных.
И последнее, что
нужно сделать – это связать класс ProfileUser с маршрутом (в
файле users/urls.py):
path('profile/<int:pk>/', views.ProfileUser.as_view(), name='profile'),
Обратите
внимание на обязательный параметр pk, по которому выбирается запись
из БД по текущему пользователю.
Однако описанный
маршрут позволяет просматривать и редактировать любые профайлы, достаточно
указать идентификатор пользователя и вся информация о нем будет отображена на
странице. Поэтому мы уберем отбор записей из таблицы user по
идентификатору:
path('profile/', views.ProfileUser.as_view(), name='profile'),
а в классе
представления ProfileUser добавим следующий метод:
def get_object(self, queryset=None):
return self.request.user
Все, теперь
профайл будет открываться только для текущего пользователя, либо сделано
перенаправление на страницу авторизации для неавторизованных пользователей.
И самое
последнее, что мы сделаем на этом занятии – пропишем маршрут к профилю в
главном меню. Для этого перейдем в шаблон base.html и внесем
следующие изменения:
<li class="last"> <a href="{% url 'users:profile' %}">{{user.username}}</a> | <a href="{% url 'users:logout' %}">Выйти</a></li>
Все, самый
простой вариант профиля пользователя мы с вами сделали.
Курс по Django: https://stepik.org/a/183363