Какие существуют способы расширения стандартной модели пользователя в Django?

Ответ

В Django существует четыре основных подхода для кастомизации стандартной модели User, каждый из которых подходит для разных задач.

1. Proxy-модель

Используется, когда нужно изменить поведение модели (например, добавить новые методы или изменить стандартный менеджер), но не требуется добавлять новые поля в базу данных.

  • Преимущества: Простота реализации, не требует миграций схемы БД.
  • Сценарий: Добавить метод get_full_name() или кастомный менеджер для фильтрации пользователей.
from django.contrib.auth.models import User

class CustomUserManager(models.Manager):
    def get_staff_users(self):
        return self.get_queryset().filter(is_staff=True)

class StaffUser(User):
    objects = CustomUserManager()

    class Meta:
        proxy = True
        ordering = ('first_name',)

    def get_full_name_and_email(self):
        return f"{self.first_name} {self.last_name} <{self.email}>"

2. One-to-One связь (Профиль пользователя)

Классический способ добавить дополнительные поля, не затрагивая исходную модель User. Создается отдельная модель (например, Profile), связанная с User отношением «один к одному».

  • Преимущества: Не изменяет ядро Django, легко добавлять и удалять поля.
  • Сценарий: Хранение аватара, биографии, даты рождения.
from django.db import models
from django.contrib.auth.models import User

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    bio = models.TextField(blank=True)
    birth_date = models.DateField(null=True, blank=True)

3. Наследование от AbstractUser

Этот подход позволяет добавить поля непосредственно в таблицу пользователей, унаследовав все поля и методы стандартного User.

  • Преимущества: Все данные пользователя хранятся в одной таблице.
  • Сценарий: Добавить поле phone_number или patronymic как обязательное.
# models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    phone_number = models.CharField(max_length=20, blank=True)
    # Добавляем related_name, чтобы избежать конфликтов
    groups = models.ManyToManyField('auth.Group', related_name='customuser_set', blank=True)
    user_permissions = models.ManyToManyField('auth.Permission', related_name='customuser_set', blank=True)

Важно: После создания такой модели необходимо указать ее в settings.py: AUTH_USER_MODEL = 'yourapp.CustomUser'

4. Наследование от AbstractBaseUser

Обеспечивает максимальную гибкость. Вы создаете модель пользователя с нуля, определяя только самые базовые поля и методы для аутентификации (например, get_full_name, get_short_name).

  • Преимущества: Полный контроль над моделью, включая механизм аутентификации (например, логин по email вместо username).
  • Сценарий: Создание системы, где email является основным идентификатором.
# models.py
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager

class MyUserManager(BaseUserManager):
    # ... реализация create_user и create_superuser

class MyUser(AbstractBaseUser):
    email = models.EmailField(verbose_name='email address', max_length=255, unique=True)
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)

    objects = MyUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

Также требует регистрации в settings.py через AUTH_USER_MODEL.

Ответ 18+ 🔞

А, вот и ты, мой друг! Сидишь, значит, и думаешь: "Как бы мне эту стандартную модель User в Django так по-взрослому переделать, чтобы и функционал был, и в жопу не вылететь?" Понимаю, понимаю. Сейчас разложу тебе всё по полочкам, как есть, без прикрас и этой вашей академической зауми.

Слушай сюда, их всего четыре, этих подхода. Как четыре стихии, блядь. Выбирай по ситуации, а то потом будешь как Герасим — молчать и страдать, потому что не ту модель выбрал.

1. Proxy-модель (она же — "хитрая жопа")

Это когда тебе поля в базе менять не надо, но очень хочется, чтобы у твоего юзера были какие-то новые методы или менеджер кастомный. Типа, добавить ему метод get_full_name_and_email, чтобы красиво выводилось. Всё делается легко, миграций не требует — просто объявил и пользуешься.

Когда брать: Когда тебе просто поведение поменять, а структуру данных не трогать. Как будто ты надел на стандартного юзера новый костюм, а под ним всё тот же труселя.

from django.contrib.auth.models import User

class CustomUserManager(models.Manager):
    def get_staff_users(self):
        return self.get_queryset().filter(is_staff=True) # Вот так отфильтруешь всех админов

class StaffUser(User):
    objects = CustomUserManager()

    class Meta:
        proxy = True
        ordering = ('first_name',)

    def get_full_name_and_email(self):
        return f"{self.first_name} {self.last_name} <{self.email}>" # Красота, а не метод!

2. One-to-One связь (она же — "профиль пользователя")

Классика жанра, ёпта! Все так делали, когда Django только начинал. Берёшь стандартного User и пристёгиваешь к нему отдельную модельку Profile через связь "один к одному". Туда все доп. поля и пихаешь: аватарку, дату рождения, "о себе".

Преимущество: Не лезешь в святая святых — в саму модель юзера. Добавил поле, удалил поле — и ни у кого не сломалось. Идеально для ленивых и осторожных.

from django.db import models
from django.contrib.auth.models import User

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    bio = models.TextField(blank=True) # Пусть пишет, что он философ
    birth_date = models.DateField(null=True, blank=True) # А тут пусть дату рождения хранит

3. Наследование от AbstractUser (она же — "сын за отца")

Вот это уже серьёзнее. Ты берёшь всю начинку стандартного User и говоришь: "Папа, я буду как ты, но с татухой". То есть наследуешься и добавляешь свои поля прямо в ту же таблицу в базе. Телефон добавить? Пожалуйста. Отчество? Да хуй с ним, добавляй!

Важный момент, блядь! После того как создал такую модель, надо в settings.py прописать: AUTH_USER_MODEL = 'yourapp.CustomUser'. Иначе Django будет искать своего родного юзера, а найдёт твоего ублюдка и обосрётся.

# models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    phone_number = models.CharField(max_length=20, blank=True) # Вот, поле добавил
    # А это вот — магические строчки, чтобы не было конфликтов с группами и пермишенами. Без них может быть больно.
    groups = models.ManyToManyField('auth.Group', related_name='customuser_set', blank=True)
    user_permissions = models.ManyToManyField('auth.Permission', related_name='customuser_set', blank=True)

4. Наследование от AbstractBaseUser (она же — "я сам с усами")

А это, дружок, полный разгон и максимальная гибкость. Ты говоришь стандартному юзеру: "Пошёл на хуй, я всё сделаю сам!" Создаёшь модель практически с нуля. Хочешь, чтобы логин был по email, а не по username? Легко! Хочешь убрать поле first_name? Да пожалуйста!

Когда брать: Когда у тебя специфичные требования к аутентификации или тебе реально надо перелопатить всё. Но будь готов — это овердохуища работы. Ты сам становишься ответственным за create_user, create_superuser и прочую базовую хуйню.

# models.py
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager

class MyUserManager(BaseUserManager):
    # ... тут тебе придётся реализовать create_user и create_superuser. Сам, блядь!

class MyUser(AbstractBaseUser):
    email = models.EmailField(verbose_name='email address', max_length=255, unique=True) # Вот он, новый логин!
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)

    objects = MyUserManager()

    USERNAME_FIELD = 'email' # Говорим Django: "Смотри, друг, логин у меня теперь email!"
    REQUIRED_FIELDS = [] # А тут можно указать, какие поля ещё спрашивать при createsuperuser

И её тоже в settings.py прописать не забудь! AUTH_USER_MODEL = 'yourapp.MyUser'.

Вот и весь расклад, блядь. Выбирай с умом, не торопись. А то потом будешь как тот мудак Герасим — смотреть на утопленную Муму и понимать, что всё пошло по пизде. Удачи!