Переопределение полей стандартной системы аутентификации пользователя Django

lepiloff

March 5, 2018, 10:35 a.m.

Стандартная джановская модель пользователя (User Model) содержит обязательное поле username. Во многих проектах есть необходимость использовать вместо него email пользователя. Сейчас мы разберем как это сделать.

ВАЖНО!!! Все последующие настройки необходимо выполнить в самом начале проекта, до запуска первой миграции. Переопределение полей в рабочем проекте более трудная задача и выходит за рамки данной статьи.

Этапы:

  1. Создадим новое приложение (не обязательно).
  2. Создадим собственную модель пользователя (User Model).
  3. Заменим дефолтную джанговскую модель собственной.
  4. Создадим собственный менеджер для новой модели.
  5. Зарегистрируем новую модель в админке.
  6. Произведем миграцию.
  7. Создадим суперпользователя.

Создаем новое приложение

python manage.py startapp YOUR_APP

Эта команда запускает создание нового приложения со следующей файловой структоруй:

YOUR_APP
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│   └── __init__.py
├── models.py
├── tests.py
└── views.py

Добавляем приложение в файл settings.py

INSTALLED_APPS = [
    # Default django installed apps
    'django.contrib.admin',
    ...
    ...
    'YOUR_APP',
]

Создаем собственную модель пользователя

Когда наше приложение создано и готово к использованию, можно перейти к определению модели User в файле models.py

from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import ugettext_lazy as _

class User(AbstractUser):
    """User model."""
    username = None
    email = models.EmailField(_('email address'), unique=True)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

Здесь мы:

  • Расширили базовый клас модели пользователя Django
  • Удалили поле username
  • Сделали поле email уникальным и обязательным
  • Сообщили django, что необходимо использовать поле email  в качестве основного для идентификации пользователя
  • Удали поле email из REQUIRED_FIELDS, т.к. этот параметр не должен включать переменные определенные в USERNAME_FIELD 

Заменяем стандартную джанговскую модель User своей

Для этого добавим в settings.py параметр AUTH_USER_MODEL

## ... некоторые другие настройки ... ##

AUTH_USER_MODEL = 'YOUR_APP.User'

## ... остальные настройки... ###

Создание собственного менеджера для новой модели пользователя

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

Расширим нашу модель пользователя в файле models.py

from django.contrib.auth.models import AbstractUser, BaseUserManager
from django.db import models
from django.utils.translation import ugettext_lazy as _


class UserManager(BaseUserManager):
    """Define a model manager for User model with no username field."""

    use_in_migrations = True

    def _create_user(self, email, password, **extra_fields):
        """Create and save a User with the given email and password."""
        if not email:
            raise ValueError('The given email must be set')
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email, password=None, **extra_fields):
        """Create and save a regular User with the given email and password."""
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        """Create and save a SuperUser with the given email and password."""
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, **extra_fields)


class User(AbstractUser):
    """User model."""
     ...
     ...
    objects = UserManager() ## это новое поле в  User model. ##

Здесь мы:

  • Расширили стандартный User Manager
  • Определили 3 метода которые использует стандартная модель
  • Не используем username ни в одном из методов
  • Выполняем проверку на существование поля email при создании User
  • Назначили новый мэнеджер новой модели

Регистрируем новую модель в админке

При использовании стандартной админки необходимо подключить новое поле email для отображения в профиле User

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
from django.utils.translation import ugettext_lazy as _
from .models import User


@admin.register(User)
class UserAdmin(DjangoUserAdmin):
    """Define admin model for custom User model with no email field."""

    fieldsets = (
        (None, {'fields': ('email', 'password')}),
        (_('Personal info'), {'fields': ('first_name', 'last_name')}),
        (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
                                       'groups', 'user_permissions')}),
        (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
    )
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'password1', 'password2'),
        }),
    )
    list_display = ('email', 'first_name', 'last_name', 'is_staff')
    search_fields = ('email', 'first_name', 'last_name')
    ordering = ('email',)

Здесь мы:

  • Расширили исходный класс UserAdmin, предоставляемый администратором Django
  • Заменили username на email
  • Зарегистрировали наш новый класс для использования в админке новыой модели пользователя

Осталось выполнить миграцию

python manage.py migrate

И создать суперпользователя

python manage.py createsuperuser

Бонус

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

Добавим в нашу модель User поле phone

from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import ugettext_lazy as _

class User(AbstractUser):
    """User model."""
    username = None
    email = models.EmailField(_('email address'), unique=True)
    phone = models.CharField(max_length=20, blank=True)
    
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

Затем  в поле Meta в форме регистрации в файле forms.py приложения которое отвечает за регистрацию (это не обязательно созданное нами приложение, поэтому поаккуратнее с импортами)

from django import forms
from django.contrib.auth.forms import UserCreationForm
from YOUR_APP.models import User

class SignUpForm(UserCreationForm):

    class Meta:
        model = User
        fields = ('phone', 'email', 'password1', 'password2',)

Теперь на странице регистрации у нас будет отображаться 4 поля: электронная почта, телефон, и 2 поля с паролем. А в админке внутри зарегистрированного пользователя  появится поле phone.

django