Шаг 02. БД, кастомная модель User и первая миграция
⚡ Кратко: что делаем на этом шаге
Цель: Создать apps/users/, написать кастомную модель User (AbstractUser + email + bio + avatar), установить AUTH_USER_MODEL = "users.User" ДО первой миграции, применить миграции, создать суперпользователя.
- Файлы:
apps/users/models.py,apps/users/admin.py,config/settings.py - Команды:
python manage.py startapp users apps/users,python manage.py makemigrations,python manage.py migrate,python manage.py createsuperuser - Результат: Django Admin доступен; суперпользователь создан через кастомную модель
🎯 Цель этапа
Кастомная модель пользователя — это одно из самых важных решений, которое нужно
принять до первой миграции. Если использовать стандартный
auth.User, в будущем будет невозможно добавить поля без болезненной
миграции всей базы. Хорошая практика: всегда начинать Django-проект с кастомного User.
auth_user, django_session и т.д.)
при первом manage.py migrate. Если вы запустили migrate со стандартным User,
а потом хотите переключиться — нужно дропнуть базу и начать заново. В этом проекте
мы делаем всё правильно: кастомный User → AUTH_USER_MODEL → потом migrate.
После этого шага у нас будет
- Приложение
apps/users/с кастомной модельюUser AUTH_USER_MODEL = "users.User"в settings.py- Все миграции применены (встроенные + наши)
- Суперпользователь создан, Django Admin открывается в браузере
📄 Затрагиваемые файлы
| Файл | Действие | Описание |
|---|---|---|
apps/users/__init__.py | Создать (startapp) | Пакет приложения users |
apps/users/apps.py | Изменить | name = "apps.users" |
apps/users/models.py | Заполнить | Кастомная модель User |
apps/users/admin.py | Заполнить | Регистрация кастомного User в Admin |
apps/users/migrations/0001_initial.py | Создаётся makemigrations | Первая миграция users |
config/settings.py | Изменить | AUTH_USER_MODEL, INSTALLED_APPS += "apps.users" |
🔨 Шаги
1. Создаём приложение users
# Создаём приложение внутри папки apps/
python manage.py startapp users apps/users
startapp users apps/users — первый аргумент (users) это
имя приложения, второй (apps/users) — путь, куда создать файлы.
После создания нужно поправить apps.py, чтобы name совпадал с путём в INSTALLED_APPS.
2. Правим apps/users/apps.py
# apps/users/apps.py
from django.apps import AppConfig
class UsersConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "apps.users" # Полный путь — важно!
verbose_name = "Пользователи"
3. Пишем кастомную модель User
# apps/users/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
"""
Кастомная модель пользователя.
Расширяем AbstractUser дополнительными полями.
Используем email как USERNAME_FIELD для входа через JWT.
username оставляем (inherited) для совместимости с Admin.
"""
email = models.EmailField(
"Email",
unique=True,
help_text="Используется для входа в систему.",
)
bio = models.TextField(
"О себе",
blank=True,
help_text="Краткое описание пользователя.",
)
avatar = models.URLField(
"Аватар (URL)",
blank=True,
help_text="Ссылка на изображение аватара.",
)
# Входим по email, не по username
USERNAME_FIELD = "email"
# username обязателен, но не используется при аутентификации
REQUIRED_FIELDS = ["username"]
class Meta:
verbose_name = "Пользователь"
verbose_name_plural = "Пользователи"
ordering = ["email"]
def __str__(self) -> str:
return self.email
REQUIRED_FIELDS — список полей, запрашиваемых при createsuperuser
(не включает USERNAME_FIELD и password).
4. Регистрируем User в Admin
# apps/users/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .models import User
@admin.register(User)
class UserAdmin(BaseUserAdmin):
"""
Наследуем от встроенного UserAdmin, чтобы сохранить
форму изменения пароля и другую стандартную функциональность.
"""
list_display = ("email", "username", "is_active", "is_staff", "date_joined")
list_filter = ("is_active", "is_staff", "is_superuser")
search_fields = ("email", "username")
ordering = ("email",)
# Добавляем наши поля в форму редактирования
fieldsets = BaseUserAdmin.fieldsets + (
("Профиль", {"fields": ("bio", "avatar")}),
)
5. Добавляем в settings.py
# config/settings.py — изменения
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# Сторонние
"rest_framework",
# Наши приложения
"apps.users", # ← добавляем
]
# ...
# КРИТИЧНО: задать ДО первой миграции!
AUTH_USER_MODEL = "users.User"
AUTH_USER_MODEL = "users.User" нужно добавить в settings.py
до запуска python manage.py migrate. Если сначала запустить
migrate (создадутся таблицы с auth_user), а потом поменять модель — Django
выдаст ошибку и база данных потребует пересоздания.
6. Создаём и применяем миграции
# Создаём миграцию для нашего User
python manage.py makemigrations users
# Применяем ВСЕ миграции (встроенные Django + наша)
python manage.py migrate
Ожидаемый вывод migrate (фрагмент):
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, users
Running migrations:
Applying contenttypes.0001_initial... OK
Applying users.0001_initial... OK
Applying admin.0001_initial... OK
...
Applying sessions.0001_initial... OK
7. Создаём суперпользователя
python manage.py createsuperuser
Django запросит: Email (наш USERNAME_FIELD), Username (из REQUIRED_FIELDS), Password.
8. Запускаем сервер и проверяем Admin
python manage.py runserver
Открываем в браузере: http://127.0.0.1:8000/admin/
🧠 Объяснение логики
AbstractUser vs AbstractBaseUser
Django предоставляет два базовых класса для кастомного User:
- AbstractUser — наследует всё стандартное поведение (username, first_name, last_name, email, is_active, is_staff и т.д.). Мы только добавляем поля. Это 95% случаев.
- AbstractBaseUser — минималистичная основа: только password и last_login. Нужен, когда стандартные поля мешают (например, нет username вообще). Сложнее в реализации.
Для Task Manager AbstractUser идеален: сохраняем всю стандартную функциональность + добавляем bio и avatar.
Почему email как USERNAME_FIELD?
В большинстве современных API пользователи логинятся по email, не по username.
Email уникален (поле с unique=True), легко запомнить.
Устанавливая USERNAME_FIELD = "email", мы говорим Django и SimpleJWT:
«ищи пользователя по этому полю при аутентификации».
name = "apps.users" в AppConfig
Без правки apps.py Django не найдёт приложение по пути "apps.users"
в INSTALLED_APPS. Поле name в AppConfig должно
совпадать с тем, что написано в INSTALLED_APPS.
name в apps.py. Django создаст приложение
с именем "users", а INSTALLED_APPS содержит "apps.users" —
это разные строки, Django не найдёт модель.
✅ Проверка
1. Проверяем миграции
python manage.py showmigrations users
users
[X] 0001_initial
2. Проверяем через Django shell
from apps.users.models import User
from django.contrib.auth import get_user_model
# get_user_model() должен вернуть нашу кастомную модель
assert get_user_model() is User, "AUTH_USER_MODEL не установлен!"
# Проверяем суперпользователя
admin = User.objects.filter(is_superuser=True).first()
print(admin.email) # ваш email
print(admin.username)
print(type(admin)) # <class 'apps.users.models.User'>
3. Django Admin
Открываем http://127.0.0.1:8000/admin/, логинимся по email + паролю суперпользователя.
Диагностика: если что-то пошло не так
ValueError: Dependency on app with no migrations: users— не выполнилиmakemigrations usersпередmigratedjango.db.migrations.exceptions.InconsistentMigrationHistory— migrate был запущен до установки AUTH_USER_MODEL. Удалитеdb.sqlite3и запуститеmigrateзаново- Admin показывает стандартный User, не кастомный — не добавили
AUTH_USER_MODELв settings.py или не зарегистрировали кастомный UserAdmin
➡️ Что дальше
На следующем шаге мы создаём остальные модели предметной области:
Project, Task и Comment — со всеми
FK/M2M-связями, TextChoices и полными миграциями.
- Готово: кастомный User, AUTH_USER_MODEL, первая миграция, суперпользователь, Admin открывается
- Далее (шаг 03):
apps/projects/,apps/tasks/, все модели со связями - После шага 03 схема БД будет полностью определена