Django расширение модели User

23 Янв 2021 , 6705

Если вы создаете новый проект на Django , то я рекомендую использовать пользовательскую модель User , которая расширяется от подкласса AbstractBaseUser. Вы конечно , можете этого не делать и использовать другие стратегии , такие как:

Но по моему личному мнению расширение от подкласса  AbstractBaseUser является самым гибким способом, хоть и вначале требует определенных усилий для его реализации.

Вначале я вкратце расскажу о других стратегиях расширения модели User. Об их достоинствах и недостатках. А потом мы реализуем создание пользовательской модели с помощью расширения от подкласса AbstractBaseUser

Использование прокси-модели

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


class CustomUser(User):
    objects = PersonManager()

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

Тут мы создаем прокси-модель Person , которая наследуется от User.Внутри Meta мы указываем proxy=True , чтобы указать , что это модель является прокси. И для этой модели в базе данных не создается таблица.

Достоинством этой стратегии является простота реализации.И эту стратегию вы можете использовать не только при создании проекта , но на любом этапе жизненнего цикла вашего проекта.

Недостатком - ограниченность, так как мы не можем создавать новые поля или переопределять существующие поля.

Использование связи один-к-одному с пользовательской моделью

Если нам нужно хранить дополнительную информацию о существующей модели пользователя и которая не связана с аутентификацией , то мы можем создать новую модель , которая будет связана с существующим моделем User c помощью связи один-к-одному.


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

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    avatar = models.TextField(max_length=500, blank=True)
    bith_date = models.TextField(max_length=500, blank=True)

   @receiver(post_save, sender=User)
    def create_user_profile(sender, instance, created, **kwargs):
        if created:
             Profile.objects.create(user=instance)

    @receiver(post_save, sender=User)
    def save_user_profile(sender, instance, **kwargs):
          instance.profile.save()
   

Достоинством данной стратегии является то , что мы можем создать новые поля и это можно сделать не только при создании проекта , но в любой момент времени когда вам это понадобится.

Недостатком данной стратегии является то , что у нас будет еще одна таблица с полями для пользователя и создаются лишние запросы при обращении к связанным данным. Это можно в определенной мере решить с использованием selected_related

Расширение от подкласса AbstractUser

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

Этот метод используется в том случае если вас устраивает аутентификация в том виде , которая предоставляется стандартно , но вам нужно добавить новые данные , но в отличие от прокси-модели не создаете новый класс или создание новой модели с таблицей , как при использовании стратегии использование связи один-к-одному с пользовательской моделью.

Класс django.contrib.auth.models.AbstractUser обеспечивает полную реализацию пользователя по умолчанию как абстрактную модель


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

class User(AbstractUser):
    birth_date = models.DateField(null=True, blank=True)


AUTH_USER_MODEL = 'users.User'

Расширение от подкласса AbstractBaseUser

Ну и в конце я создам пользовательскую модель путем расширения от класса AbstractBaseUser. Этот способ считается сложным , а некоторые советуют его применять в крайнем случае, но по мне для сложных проектов этот способ является самым предпочитаемым. Но это дело вкуса.

Часто в моих проектах в качестве логина используется либо email или номер телефона. А поле username нам вообще не нужен. Так что мы при создании модели , который наследуются от AbstractBaseUser , можем указать поле , который используется в качестве логина. Хотя , я видел статью где это можно сделать и при расширении от класса AbstractUser.

Ну довольно теории. Создадим виртуальное окружение , установим Django и создадим новый проект.

Создадим новое приложение users и в файле models.py создадим модель User , который будет наследоваться от AbstractBaseUser и от миксина PermissionsMixin


import os
import datetime
import os.path
from django.db import models
from django.conf import settings
from django.utils import timezone
from django.core.validators import RegexValidator

from django.contrib.auth.models import (
    BaseUserManager, AbstractBaseUser, PermissionsMixin
)


class UserManager(BaseUserManager):

    def create_user(self, email, password=None,**kwargs):
        if not email:
            raise ValueError('Users must have an Email')

        user = self.model(
            email=email,**kwargs)

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password):
        """
        Creates and saves a superuser with the given email and password.
        """
        user = self.create_user(
            email,
            password=password
        )
        user.is_admin = True
        user.save(using=self._db)
        return user


class User(AbstractBaseUser,PermissionsMixin):
    email = models.EmailField(max_length=255, unique=True)
    first_name = models.CharField('Фамилия', max_length=255, blank=True, null=True)
    last_name = models.CharField('Имя', max_length=255, blank=True, null=True)
    avatar = models.ImageField(null=True, blank=True, upload_to='avatars')
    date_of_birth = models.DateField(verbose_name="Дата рождения", null=True, blank=True)
    last_time_visit = models.DateTimeField(default=timezone.now)
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)

    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    def __str__(self):
        return self.email

    class Meta:
        verbose_name = 'Пользователь'
        verbose_name_plural = 'Пользователи'


Создайте миграции для users. Важно , это вы должны сделать до применения первоначальных миграций


python manage.py makemigrations

Применяем все миграции проекта


python manage.py migrate

Заключение

Для простых проектов вы можете использовать встроенную модель без расширения, так как он из коробки работает прекрасно и выполняет базовые вещи. Для простых сайтов , где не нужна особо работа с пользователями , то применятся вариант из коробки. Для этого блога это вполне хватает ))

Но если вы разрабатываете сложные проекты , то я рекомендую расширять пользовательскую модель. Вначале это сложно и требует некоторых усилий , но в дальнейшем это окупится.

comments powered by Disqus

Подписка

Подпишитесь на наш список рассылки, чтобы получать обновления из блога

Рубрики

Теги