Курс по Django: https://stepik.org/a/183363
Архив проекта: 71_sitewomen.zip
На данный момент
мы с вами разобрали все основные компоненты, связанные с авторизацией
пользователя на сайте: непосредственно авторизация, регистрация, восстановление
и изменение пароля, создали профиль пользователя со стандартной моделью User. Если мы посмотрим
на таблицу auth_user, то набор
информационных полей будет примерно таким:
- username
– логин
пользователя;
- password – пароль для
входа в систему;
- email
– электронная
почта;
- first_name
– имя
пользователя;
- last_name
– фамилия
пользователя.
Но, что если нам
дополнительно нужно хранить фотографию пользователя, его дату рождения, пол,
город, семейное положение и так далее. Как это сделать? Как расширить уже
существующую модель User? Во фреймворке Django для этой цели
используют два разных подхода:
- создание еще
одной модели (например, Profile) со связью one-to-one (один к одному)
с моделью User;
- создание новой
модели User на базе
специального класса AbstractUser фреймворка Django.
У каждого
подхода есть свои преимущества и недостатки. Преимущества первого состоят в
использовании стандартной модели User, а значит, все модули, которые напрямую
обращаются к ней, продолжат работать без каких-либо переработок. А основной
недостаток – это необходимость в запросах отдельно указывать связанную модель Profile. Преимущества
второго подхода, очевидно, в том, что мы, как и ранее, продолжаем работать с
одной моделью пользователя, а значит, скорость обработки запросов, в среднем,
несколько возрастает. И, кроме того, нам не нужно помнить о дополнительных
моделях, что удобно. Главный недостаток – это уход от стандартной модели,
поэтому нужно быть уверенным, что во всех компонентах проекта фреймворка Django обращение идет
именно к этой новой таблице.
На практике
используют оба подхода в зависимости от удобства разработки текущего проекта. Но,
на мой взгляд, при прочих равных, предпочтение следует отдавать второму
подходу, так мы получаем более нативное решение и, по-прежнему, привычно
работаем с одной моделью. Именно расширение стандартной модели с помощью
базового класса AbstractUser мы с вами и рассмотрим на этом занятии.
Ну а первый вариант можно реализовать самостоятельно в качестве домашнего
задания (все знания у вас уже есть).
Класс AbstractUser
Согласно
документации Django:
https://docs.djangoproject.com/en/4.2/topics/auth/customizing/#extending-the-existing-user-model
мы можем
расширить существующую модель User следующим образом (в файле users/models.py):
class User(AbstractUser):
photo = models.ImageField(upload_to="users/%Y/%m/%d/", blank=True, null=True, verbose_name="Фотография")
date_birth = models.DateTimeField(blank=True, null=True, verbose_name="Дата рождения")
Также в
документации сказано, что в файле users/admin.py необходимо
зарегистрировать эту новую модель для админ-панели следующим образом:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from users.models import User
admin.site.register(User, UserAdmin)
Все, наша модель
создана и зарегистрирована. Причем, в ней будут присутствовать все стандартные
поля прежней модели User, благодаря наследованию от класса AbstractUser, и два
дополнительных поля: photo и date_birth. Однако если мы
сейчас попробуем создать миграцию для создания новой таблицы user, то увидим
ошибки. Это связано с тем, что при замене существующей модели User на свою
собственную, нам в файле settings.py необходимо еще
переопределить параметр AUTH_USER_MODEL, который по умолчанию принимает
значение 'auth.User'. В нашем случае параметр AUTH_USER_MODEL следует прописать
так:
AUTH_USER_MODEL = 'users.User'
То есть, вначале
указывается имя приложения, а затем, через точку, имя используемой модели в
текущем проекте фреймворка Django. Теперь при создании миграции:
python manage.py
makemigrations
никаких ошибок
не возникает. Но применение созданной миграции:
python manage.py
migrate
снова приводит к
ошибкам. Это из-за того, что прежняя модель User в БД связана с
другими таблицами. Нам придется удалить все файлы миграций из приложений users и women, а также файл
базы данных.
Такие
радикальные шаги приходится делать, так как целиком замещается стандартная
модель User на совершенно
другую из приложения users. Чтобы таких неудобств не возникало,
следует заранее продумывать вопрос о расширении стандартной модели. Тогда
ничего удалять не придется. Причем, число и тип полей в новой расширенной
модели мы впоследствии сможем изменить совершенно спокойно без удаления файлов
миграций. Только замена модели целиком приводит к переделке всей БД.
Итак, удаляем
все миграции из приложений users и women, удаляем файл
БД и снова выполняем команды:
python manage.py makemigrations
python
manage.py migrate
Теперь ошибок
никаких нет и все таблицы были созданы успешно.
Следующим шагом следует
убрать во всем проекте обращение к стандартной модели User, если это имело
место, и вместо нее прописать функцию get_user_model().
Если при попытке
запуска проекта возникает ошибка:
AUTH_USER_MODEL refers to model 'users.get_user_model()' that has not been installed
то в файле settings.py нужно проверить
определение параметра AUTH_USER_MODEL, которое должно быть таким:
AUTH_USER_MODEL = 'users.User'
Все, проект у
нас с вами запустился с полным функционалом сайта, но, увы, без содержимого.
Вначале нам нужно снова создать суперюзера:
python
manage.py createsuperuser
Ввести прежние
данные и пробуем войти в систему. Как видите, все работает, но уже с новой
расширенной таблицей User. Попробуем зарегистрироваться:
user1; sc_lib@list.ru; Sergey; Balakirev; root12345
Нажимаем на
кнопку «Регистрация» и попадаем на страницу авторизации. Попробуем
авторизоваться по E-mail и паролю:
sc_lib@list.ru; root12345
Успешно входим в
систему и видим пользователя user1. Все работает.
Далее, я вручную
через админ-панель добавлю данные в наши таблицы, чтобы сайт вновь приобрел
свое содержимое.
Последним
штрихом добавим отображение даты рождения и фотографию пользователя в его профиле.
Сначала в файле users/forms.py в классе ProfileUserForm
сделаем отображение даты рождения:
class ProfileUserForm(forms.ModelForm):
...
this_year = datetime.date.today().year
date_birth = forms.DateField(widget=forms.SelectDateWidget(years=tuple(range(this_year-100, this_year-5))))
class Meta:
model = get_user_model()
fields = ['photo', 'username', 'email', 'date_birth', 'first_name', 'last_name']
...
Чтобы воспользоваться
модулем datetime вначале его
следует импортировать:
Осталось сделать
отображение текущего изображения в профиле пользователя. Для этого перейдем в
файл шаблона users/profile.html и добавим в
него строчки:
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% if user.photo %}
<p ><img src="{{ user.photo.url }}">
{% else %}
<p ><img src="/media/users/default.png">
{% endif %}
...
Соответственно,
в каталоге media в папке users следует разместить
файл default.png.
Однако явно
прописывать путь к default.png не лучшая
практика. Если путь изменится, придется править все строчки, где он упоминается
в шаблонах. Поэтому мы через параметр extra_context класса ProfileUser (в
файле users/views.py) передадим этот
маршрут следующим образом:
extra_context = {'title': "Профиль пользователя", 'default_image': settings.DEFAULT_USER_IMAGE}
А параметр DEFAULT_USER_IMAGE определим в файле
settings.py нашего проекта:
DEFAULT_USER_IMAGE = MEDIA_URL + 'users/default.png'
Теперь в шаблоне
profile.html достаточно
прописать переменную default_image:
<p ><img src="{{ default_image }}">
Все, расширенный
профиль у нас с вами готов.
Курс по Django: https://stepik.org/a/183363