Spec-Zone.ru › Django 4.0

Настройка аутентификации в Django

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

Бэкенды аутентификации предоставляют расширяемую систему для случаев, когда имя пользователя и пароль, хранящиеся в модели пользователя, должны быть проверены на другом сервисе, отличном от стандартного Django.

Вы можете предоставить своим моделям собственные разрешения, которые могут быть проверены через систему авторизации Django.

Вы можете расширить стандартную User модель или заменить её полностью настроенной моделью.

Другие источники аутентификации

Иногда требуется подключение к другому источнику аутентификации — то есть, к другому источнику имён пользователей, паролей или методам аутентификации.

Например, ваша компания может уже иметь установленный LDAP, хранящий имя пользователя и пароль каждого сотрудника. Было бы неудобно как для системного администратора, так и для пользователей, если бы у них были отдельные учётные записи в LDAP и приложениях на базе Django.

Поэтому, чтобы справиться с такими ситуациями, система аутентификации Django позволяет подключать другие источники аутентификации. Вы можете переопределить стандартную базу данных Django или использовать стандартную систему вместе с другими системами.

См. справочник по бэкендам аутентификации для получения информации о бэкендах аутентификации, включенных в Django.

Указание бэкендов аутентификации

Внутри Django поддерживается список «бэкендов аутентификации», которые проверяются при аутентификации. Когда кто-то вызывает django.contrib.auth.authenticate() — как описано в Как войти в систему — Django пытается выполнить аутентификацию через все свои бэкенды аутентификации. Если первый метод аутентификации терпит неудачу, Django пытается второй и так далее, пока не будут испробованы все бэкенды.

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

По умолчанию, AUTHENTICATION_BACKENDS имеет значение:

['django.contrib.auth.backends.ModelBackend']

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

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

Если бэкенд вызывает исключение PermissionDenied, аутентификация немедленно завершится неудачей. Django не будет проверять последующие бэкенды.

Примечание

После аутентификации пользователя Django сохраняет, какой бэкенд был использован для аутентификации пользователя в сессии и повторно использует тот же бэкенд в течение этой сессии всякий раз, когда требуется доступ к текущему аутентифицированному пользователю. Это эффективно означает, что источники аутентификации кешируются на основе сессии, поэтому если вы измените AUTHENTICATION_BACKENDS, вам необходимо очистить данные сессии, если вы хотите принудительно заставить пользователей повторно пройти аутентификацию с использованием разных методов. Простой способ сделать это — выполнить Session.objects.all().delete().

Написание бэкенда аутентификации

Бэкенд аутентификации — это класс, который реализует два обязательных метода: get_user(user_id) и authenticate(request, **credentials), а также набор необязательных методов, связанных с разрешениями методов авторизации.

Метод get_user принимает user_id — это может быть имя пользователя, идентификатор базы данных или что-либо ещё, но должно быть первичным ключом вашего объекта пользователя — и возвращает объект пользователя или None.

Метод authenticate принимает аргумент request и данные для аутентификации в качестве ключевых аргументов. В большинстве случаев это выглядит так:

from django.contrib.auth.backends import BaseBackend

class MyBackend(BaseBackend):
    def authenticate(self, request, username=None, password=None):
        # Check the username/password and return a user.
        ...

Но также может аутентифицировать токен, например:

from django.contrib.auth.backends import BaseBackend

class MyBackend(BaseBackend):
    def authenticate(self, request, token=None):
        # Check the token and return a user.
        ...

В любом случае, authenticate() должен проверить полученные данные для аутентификации и вернуть объект пользователя, соответствующий этим данным, если они валидны. Если данные не валидны, он должен вернуть None.

request является HttpRequest и может быть None если он не был передан в authenticate() (который передаёт его бэкенду).

Админка Django тесно связана с объектом пользователя Django. Лучший способ справиться с этим — создать объект Django User для каждого пользователя, который существует для вашего бэкенда (например, в вашем каталоге LDAP, вашей внешней базе данных SQL и т. д.). Вы можете либо написать скрипт для этого заранее, либо ваш метод authenticate может сделать это при первом входе пользователя в систему.

Вот пример бэкенда, который выполняет аутентификацию по имени пользователя и паролю, определённому в файле settings.py и создаёт объект Django User при первой аутентификации пользователя:

from django.conf import settings
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User

class SettingsBackend(BaseBackend):
    """
    Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.

    Use the login name and a hash of the password. For example:

    ADMIN_LOGIN = 'admin'
    ADMIN_PASSWORD = 'pbkdf2_sha256$30000$Vo0VlMnkR4Bk$qEvtdyZRWTcOsCnI/oQ7fVOu1XAURIZYoOZ3iq8Dr4M='
    """

    def authenticate(self, request, username=None, password=None):
        login_valid = (settings.ADMIN_LOGIN == username)
        pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
        if login_valid and pwd_valid:
            try:
                user = User.objects.get(username=username)
            except User.DoesNotExist:
                # Create a new user. There's no need to set a password
                # because only the password from settings.py is checked.
                user = User(username=username)
                user.is_staff = True
                user.is_superuser = True
                user.save()
            return user
        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

Обработка авторизации в пользовательских бэкендах

Пользовательские бэкенды аутентификации могут предоставлять собственные разрешения.

Модель пользователя и её менеджер делегируют функции поиска разрешений (get_user_permissions(), get_group_permissions(), get_all_permissions(), has_perm(), has_module_perms() и with_perm()) любому бэкенду аутентификации, который реализует эти функции.

Разрешения, предоставленные пользователю, будут представлять собой объединение всех разрешений, возвращённых всеми бэкендами. То есть, Django предоставляет разрешение пользователю, если такое разрешение предоставляет любой из бэкендов.

Если бэкенд вызывает исключение PermissionDenied в has_perm() или has_module_perms(), авторизация немедленно завершится неудачей, и Django не будет проверять последующие бэкенды.

Бэкенд может реализовать разрешения для магической админки так:

from django.contrib.auth.backends import BaseBackend

class MagicAdminBackend(BaseBackend):
    def has_perm(self, user_obj, perm, obj=None):
        return user_obj.username == settings.ADMIN_LOGIN

Это предоставляет полные разрешения пользователю, которому предоставлен доступ в вышеприведённом примере. Обратите внимание, что помимо тех же аргументов, которые передаются связанным функциям django.contrib.auth.models.User, функции авторизации бэкенда также принимают объект пользователя, который может быть анонимным пользователем, в качестве аргумента.

Полная реализация авторизации находится в классе ModelBackend в django/contrib/auth/backends.py, который является стандартным бэкендом и в большинстве случаев запрашивает таблицу auth_permission.

Авторизация для анонимных пользователей

Анонимный пользователь — это пользователь, который не прошёл аутентификацию, то есть он не предоставил никаких валидных данных для аутентификации. Однако это не обязательно означает, что он не имеет права на выполнение каких-либо действий. В большинстве случаев веб-сайты разрешают анонимным пользователям просматривать большую часть сайта, и многие позволяют анонимно оставлять комментарии и т. д.

В рамках системы разрешений Django нет места для хранения разрешений анонимных пользователей. Однако объект пользователя, передаваемый бэкенду аутентификации, может быть объектом django.contrib.auth.models.AnonymousUser, позволяя бэкенду указать пользовательское поведение авторизации для анонимных пользователей. Это особенно полезно для авторов повторно используемых приложений, которые могут делегировать все вопросы авторизации бэкенду аутентификации, а не, например, нуждаться в настройках для управления доступом анонимных пользователей.

Авторизация неактивных пользователей

Неактивный пользователь — это пользователь, у которого поле is_active установлено в значение False. Модули аутентификации ModelBackend и RemoteUserBackend запрещают этим пользователям аутентификацию. Если пользовательская модель не содержит поля is_active, все пользователи смогут пройти аутентификацию.

Вы можете использовать AllowAllUsersModelBackend или AllowAllUsersRemoteUserBackend, если хотите разрешить аутентификацию неактивных пользователей.

Поддержка анонимных пользователей в системе разрешений позволяет сценарий, когда анонимные пользователи имеют разрешения на выполнение определенных действий, в то время как неактивные авторизованные пользователи — нет.

Не забудьте проверить атрибут is_active пользователя в собственных методах проверки разрешений.

Обработка разрешений на объекты

В рамках фреймворка разрешений Django заложена основа для разрешений на объекты, хотя в ядре реализации нет. Это означает, что проверка разрешений на объекты всегда вернет False или пустой список (в зависимости от выполненной проверки). Модуль аутентификации получит ключевые параметры obj и user_obj для каждого метода авторизации, связанного с объектом, и сможет вернуть разрешение на уровне объекта соответственно.

Настраиваемые разрешения

Для создания настраиваемых разрешений для заданного объекта модели используйте атрибут permissions атрибут модели Meta.

В этом примере Task модель создает два настраиваемых разрешения, то есть действия, которые пользователи могут или не могут выполнять с Task экземплярами, специфичными для вашего приложения:

class Task(models.Model):
    ...
    class Meta:
        permissions = [
            ("change_task_status", "Can change the status of tasks"),
            ("close_task", "Can remove a task by setting its status as closed"),
        ]

Единственное, что это делает, — создает эти дополнительные разрешения при запуске manage.py migrate (функция, которая создаёт разрешения, связана со сигналом post_migrate). Ваш код отвечает за проверку значения этих разрешений, когда пользователь пытается получить доступ к функциональности, предоставляемой приложением (изменение статуса задач или закрытие задач). Продолжая приведенный выше пример, следующее проверяет, может ли пользователь закрыть задачи:

user.has_perm('app.close_task')

Расширение существующей User модели

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

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

from django.contrib.auth.models import User

class Employee(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    department = models.CharField(max_length=100)

Предполагая, что у существующего сотрудника Фреда Смита есть как модель User, так и модель Employee, вы можете получить доступ к связанной информации, используя стандартные соглашения Django для связанных моделей:

>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department

Чтобы добавить поля модели профиля на страницу пользователя в админке, определите InlineModelAdmin (в этом примере мы будем использовать StackedInline) в admin.py вашего приложения и добавьте его в класс UserAdmin, который зарегистрирован с классом User:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User

from my_user_profile_app.models import Employee

# Define an inline admin descriptor for Employee model
# which acts a bit like a singleton
class EmployeeInline(admin.StackedInline):
    model = Employee
    can_delete = False
    verbose_name_plural = 'employee'

# Define a new User admin
class UserAdmin(BaseUserAdmin):
    inlines = (EmployeeInline,)

# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)

Эти модели профиля ничем не отличаются — это просто модели Django, которые случайно связаны один-к-одному с моделью пользователя. В связи с этим они не создаются автоматически при создании пользователя, но можно использовать django.db.models.signals.post_save для создания или обновления связанных моделей по мере необходимости.

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

Замена настраиваемой User моделью

В некоторых проектах требования к аутентификации могут быть такими, что встроенная модель User Django не всегда подходит. Например, на некоторых сайтах логичнее использовать адрес электронной почты в качестве маркера идентификации вместо имени пользователя.

Django позволяет переопределить модель пользователя по умолчанию, указав значение для настройки AUTH_USER_MODEL, которая ссылается на настраиваемую модель:

AUTH_USER_MODEL = 'myapp.MyUser'

Эта пара через точку описывает label приложения Django (которое должно быть в вашем INSTALLED_APPS), и имя модели Django, которую вы хотите использовать в качестве модели пользователя.

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

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

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    pass

Не забудьте указать AUTH_USER_MODEL на неё. Сделайте это до создания миграций или запуска manage.py migrate в первый раз.

Также зарегистрируйте модель в admin.py приложения:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User

admin.site.register(User, UserAdmin)

Изменение настраиваемой модели пользователя в процессе работы над проектом

Изменение AUTH_USER_MODEL после создания таблиц базы данных значительно сложнее, так как это влияет на внешние ключи и связи «многие ко многим», например.

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

Из-за ограничений функции динамической зависимости Django для взаимозаменяемых моделей, модель, на которую ссылается AUTH_USER_MODEL, должна быть создана в первой миграции своего приложения (обычно она называется 0001_initial); в противном случае у вас возникнут проблемы с зависимостями.

Кроме того, при запуске миграций может возникнуть CircularDependencyError, так как Django не сможет автоматически разорвать цикл зависимости из-за динамической зависимости. Если вы увидите эту ошибку, вы должны разорвать цикл, перенеся модели, от которых зависит ваша модель пользователя, во вторую миграцию. (Вы можете попробовать создать две обычные модели, у которых есть ForeignKey друг к другу и посмотреть, как makemigrations решит эту циклическую зависимость, если хотите увидеть, как это обычно делается.)

Модули повторного использования и модель пользователя аутентификации

Модули повторного использования не должны реализовывать настраиваемую модель пользователя. Проект может использовать множество приложений, и два модуля повторного использования, реализующие настраиваемую модель пользователя, не могут быть использованы вместе. Если вам нужно хранить информацию о пользователе в вашем приложении, используйте ForeignKey или OneToOneField к settings.AUTH_USER_MODEL, как описано ниже.

Ссылка на модель User

Если вы напрямую ссылаетесь на User (например, ссылаясь на него в внешнем ключе), ваш код не будет работать в проектах, где параметр AUTH_USER_MODEL был изменён на другую модель пользователя.

get_user_model()

Вместо прямой ссылки на User, вы должны ссылаться на модель пользователя, используя django.contrib.auth.get_user_model(). Этот метод вернёт текущую активную модель пользователя — пользовательскую модель, если она указана, или User в противном случае.

При определении внешних ключей или связей многие-ко-многим с моделью пользователя, вы должны указать пользовательскую модель, используя параметр AUTH_USER_MODEL. Например:

from django.conf import settings
from django.db import models

class Article(models.Model):
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )

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

from django.conf import settings
from django.db.models.signals import post_save

def post_save_receiver(sender, instance, created, **kwargs):
    pass

post_save.connect(post_save_receiver, sender=settings.AUTH_USER_MODEL)

В общем случае, проще всего ссылаться на модель пользователя с помощью параметра AUTH_USER_MODEL в коде, который выполняется во время импорта. Однако, также возможно вызвать get_user_model() в то время, как Django импортирует модели, поэтому вы можете использовать models.ForeignKey(get_user_model(), ...).

Если ваш приложение тестируется с несколькими моделями пользователей, например, с @override_settings(AUTH_USER_MODEL=...), и вы кэшируете результат get_user_model() в переменной уровня модуля, вам может потребоваться прослушивать сигнал setting_changed для очистки кэша. Например:

from django.apps import apps
from django.contrib.auth import get_user_model
from django.core.signals import setting_changed
from django.dispatch import receiver

@receiver(setting_changed)
def user_model_swapped(**kwargs):
    if kwargs['setting'] == 'AUTH_USER_MODEL':
        apps.clear_cache()
        from myapp import some_module
        some_module.UserModel = get_user_model()

Определение пользовательской модели

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

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

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

Самый простой способ создать совместимую пользовательскую модель — унаследовать её от AbstractBaseUser. AbstractBaseUser предоставляет основную реализацию модели пользователя, включая хэшированные пароли и токенизированные сбросы паролей. Затем вам нужно предоставить некоторые ключевые детали реализации:

class models.CustomUser
USERNAME_FIELD

Строка, описывающая имя поля в модели пользователя, которое используется в качестве уникального идентификатора. Обычно это имя пользователя, но это также может быть адрес электронной почты или любой другой уникальный идентификатор. Поле обязательно должно быть уникальным (т.е., иметь unique=True в своём определении), если вы не используете кастомный бэкэнд аутентификации, который может поддерживать не уникальные имена пользователей.

В следующем примере поле identifier используется в качестве идентифицирующего поля:

class MyUser(AbstractBaseUser):
    identifier = models.CharField(max_length=40, unique=True)
    ...
    USERNAME_FIELD = 'identifier'
EMAIL_FIELD

Строка, описывающая имя поля электронной почты в модели User. Это значение возвращается методом get_email_field_name().

REQUIRED_FIELDS

Список имён полей, которые будут запрошены при создании пользователя через менеджер createsuperuser. Пользователь будет попрошен предоставить значение для каждого из этих полей. Он должен включать любое поле, для которого blank имеет значение False или не определено, и может включать дополнительные поля, для которых вы хотите запросить значения при интерактивном создании пользователя. REQUIRED_FIELDS не влияет на другие части Django, например, на создание пользователя в админке.

Например, вот частичное определение модели пользователя, которая определяет два требуемых поля — дату рождения и рост:

class MyUser(AbstractBaseUser):
    ...
    date_of_birth = models.DateField()
    height = models.FloatField()
    ...
    REQUIRED_FIELDS = ['date_of_birth', 'height']

Примечание

REQUIRED_FIELDS должен содержать все требуемые поля вашей модели пользователя, но не должен содержать USERNAME_FIELD или password, так как эти поля всегда будут запрошены.

is_active

Логический атрибут, указывающий, считается ли пользователь «активным». Этот атрибут предоставляется в модели AbstractBaseUser по умолчанию, равный True. Как вы выберете его реализацию, зависит от деталей выбранных вами бэкэндов аутентификации. См. документацию is_active attribute on the built-in user model для подробностей.

get_full_name()

Необязательно. Более длинный формальный идентификатор пользователя, такой как его полное имя. Если реализовано, это отображается наряду с именем пользователя в истории объекта в django.contrib.admin.

get_short_name()

Необязательно. Короткое, неофициальное обозначение пользователя, например, его имя. Если реализовано, оно заменяет имя пользователя в приветствии пользователю в заголовке django.contrib.admin.

Импорт AbstractBaseUser

AbstractBaseUser и BaseUserManager импортируемы из django.contrib.auth.base_user, чтобы их можно было импортировать без включения django.contrib.auth в INSTALLED_APPS.

Следующие атрибуты и методы доступны для любого подкласса AbstractBaseUser:

class models.AbstractBaseUser
get_username()

Возвращает значение поля, указанного в USERNAME_FIELD.

clean()

Нормализует имя пользователя, вызвав normalize_username(). Если вы переопределяете этот метод, обязательно вызовите super() для сохранения нормализации.

classmethod get_email_field_name()

Возвращает имя поля электронной почты, указанное атрибутом EMAIL_FIELD. По умолчанию возвращает 'email' если EMAIL_FIELD не указан.

classmethod normalize_username(username)

Применяет Unicode-нормализацию NFKC к именам пользователей, чтобы визуально идентичные символы с различными кодами Unicode считались одинаковыми.

is_authenticated

Только для чтения атрибут, который всегда True (в отличие от AnonymousUser.is_authenticated , который всегда False). Это способ определить, был ли пользователь аутентифицирован. Это не подразумевает никаких разрешений и не проверяет, активен ли пользователь или у него есть действительная сессия. Хотя обычно вы проверяете этот атрибут на request.user , чтобы узнать, был ли он заполнен средством AuthenticationMiddleware (представляющим текущего пользователя, авторизованного в системе), вы должны знать, что этот атрибут равен True для любого объекта User.

is_anonymous

Атрибут только для чтения, который всегда False. Это способ отличить объекты User и AnonymousUser. В целом, вы должны предпочитать использовать is_authenticated этому атрибуту.

set_password(raw_password)

Устанавливает пароль пользователя на указанную строку, обрабатывая хеширование пароля. Не сохраняет объект AbstractBaseUser.

Когда raw_password равен None, пароль будет установлен на непригодный, как если бы был использован set_unusable_password().

check_password(raw_password)

Возвращает True если указанная строка — правильный пароль для пользователя. (Это обрабатывает хеширование пароля при сравнении.)

set_unusable_password()

Помечает пользователя как не имеющего установленного пароля. Это не то же самое, что иметь пустую строку в качестве пароля. check_password() для этого пользователя никогда не вернет True. Не сохраняет объект AbstractBaseUser.

Вам может потребоваться это, если аутентификация для вашего приложения происходит по внешнему источнику, такому как каталог LDAP.

has_usable_password()

Возвращает False если для этого пользователя был вызван set_unusable_password().

get_session_auth_hash()

Возвращает HMAC поля пароля. Используется для Отмены сеанса при смене пароля.

AbstractUser наследуется от AbstractBaseUser:

class models.AbstractUser
clean()

Нормализует адрес электронной почты, вызвав BaseUserManager.normalize_email(). Если вы переопределяете этот метод, обязательно вызовите super() для сохранения нормализации.

Написание менеджера для пользовательской модели

Вам также следует определить пользовательский менеджер для вашей модели пользователя. Если ваша модель пользователя определяет поля username, email, is_staff, is_active, is_superuser, last_login, и date_joined так же, как у стандартной модели пользователя Django, вы можете использовать менеджер UserManager. Однако, если поля вашей модели пользователя отличаются, вам нужно определить пользовательский менеджер, который расширяет BaseUserManager и реализует два дополнительных метода:

class models.CustomUserManager
create_user(username_field, password=None, **other_fields)

Шаблон метода create_user() должен принимать имя пользователя, а также все необходимые поля в качестве аргументов. Например, если ваша модель пользователя использует email в качестве поля имени пользователя и имеет date_of_birth в качестве обязательного поля, тогда create_user должен быть определен как:

def create_user(self, email, date_of_birth, password=None):
    # create user here
    ...
create_superuser(username_field, password=None, **other_fields)

Шаблон метода create_superuser() должен принимать имя пользователя, а также все необходимые поля в качестве аргументов. Например, если ваша модель пользователя использует email в качестве поля имени пользователя и имеет date_of_birth в качестве обязательного поля, тогда create_superuser должен быть определен как:

def create_superuser(self, email, date_of_birth, password=None):
    # create superuser here
    ...

Для ForeignKey в USERNAME_FIELD или REQUIRED_FIELDS эти методы получают значение to_field (ключевое поле primary_key по умолчанию) существующего экземпляра.

BaseUserManager предоставляет следующие вспомогательные методы:

class models.BaseUserManager
classmethod normalize_email(email)

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

get_by_natural_key(username)

Возвращает экземпляр пользователя, используя содержимое поля, указанного в USERNAME_FIELD.

make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')

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

  • i, l, I, и 1 (строчные буквы i, L, прописные i и цифра один)
  • o, O, и 0 (строчная буква o, прописная буква O и цифра ноль)

Расширение стандартной модели пользователя Django

Если вас полностью устраивает модель пользователя Django User, но вы хотите добавить некоторую дополнительную информацию о профиле, вы можете унаследовать от django.contrib.auth.models.AbstractUser и добавить свои пользовательские поля профиля, хотя мы рекомендуем использовать отдельную модель, как описано в примечании «Уточнение модели пользователя» в Уточнение модели пользователя. AbstractUser предоставляет полную реализацию по умолчанию User в качестве абстрактной модели.

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

Встроенные формы форм и представлений Django предполагают определенные свойства модели пользователя, с которой они работают.

Следующие формы совместимы с любым подклассом AbstractBaseUser:

  • AuthenticationForm: Использует поле имени пользователя, указанное в USERNAME_FIELD.
  • SetPasswordForm
  • PasswordChangeForm
  • AdminPasswordChangeForm

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

  • PasswordResetForm: Предполагает, что модель пользователя имеет поле, хранящее адрес электронной почты пользователя с именем, возвращаемым get_email_field_name() (email по умолчанию), которое может быть использовано для идентификации пользователя, и логическое поле с именем is_active для предотвращения сброса пароля для неактивных пользователей.

И, наконец, следующие формы привязаны к User и должны быть переписаны или расширены для работы с пользовательской моделью:

  • UserCreationForm
  • UserChangeForm

Если ваша пользовательская модель является подклассом AbstractUser, вы можете расширить эти формы таким образом:

from django.contrib.auth.forms import UserCreationForm
from myapp.models import CustomUser

class CustomUserCreationForm(UserCreationForm):

    class Meta(UserCreationForm.Meta):
        model = CustomUser
        fields = UserCreationForm.Meta.fields + ('custom_field',)

Пользователи по заказу и django.contrib.admin

Если вы хотите, чтобы ваша пользовательская модель также работала с администратором, ваша модель пользователя должна определять дополнительные атрибуты и методы. Эти методы позволяют администратору контролировать доступ пользователя к контенту администратора:

class models.CustomUser
is_staff

Возвращает True если пользователю разрешен доступ к сайту администратора.

is_active

Возвращает True если учетная запись пользователя в настоящее время активна.

has_perm(perm, obj=None):

Возвращает True если пользователь имеет указанное разрешение. Если obj указано, разрешение необходимо проверить для конкретного экземпляра объекта.

has_module_perms(app_label):

Возвращает True если пользователю разрешен доступ к моделям в указанном приложении.

Вам также необходимо зарегистрировать вашу пользовательскую модель в администраторе. Если ваша пользовательская модель расширяет django.contrib.auth.models.AbstractUser, вы можете использовать существующий класс Django django.contrib.auth.admin.UserAdmin. Однако, если ваша модель пользователя расширяет AbstractBaseUser, вам нужно определить пользовательский класс ModelAdmin. Возможно, вы сможете унаследовать от стандартного класса django.contrib.auth.admin.UserAdmin, но вам нужно будет переопределить любые определения, которые ссылаются на поля в django.contrib.auth.models.AbstractUser, которых нет в вашем пользовательском классе.

Примечание

Если вы используете пользовательскую модель ModelAdmin, которая является подклассом django.contrib.auth.admin.UserAdmin, вам необходимо добавить свои пользовательские поля в fieldsets (для полей, используемых при редактировании пользователей) и в add_fieldsets (для полей, используемых при создании пользователя). Например:

from django.contrib.auth.admin import UserAdmin

class CustomUserAdmin(UserAdmin):
    ...
    fieldsets = UserAdmin.fieldsets + (
        (None, {'fields': ('custom_field',)}),
    )
    add_fieldsets = UserAdmin.add_fieldsets + (
        (None, {'fields': ('custom_field',)}),
    )

См. полный пример для получения дополнительной информации.

Пользователи по заказу и разрешения

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

PermissionsMixin предоставляет следующие методы и атрибуты:

class models.PermissionsMixin
is_superuser

Логическое значение. Обозначает, что этот пользователь обладает всеми правами без их явного назначения.

get_user_permissions(obj=None)

Возвращает набор строк разрешений, которые пользователь имеет напрямую.

Если obj передан, возвращает только разрешения пользователя для этого конкретного объекта.

get_group_permissions(obj=None)

Возвращает набор строк разрешений, которые у пользователя есть через свои группы.

Если obj передан, возвращает только разрешения группы для этого конкретного объекта.

get_all_permissions(obj=None)

Возвращает набор строк разрешений, которые у пользователя есть как через разрешения пользователя, так и через группы.

Если obj передан, возвращает только разрешения для этого конкретного объекта.

has_perm(perm, obj=None)

Возвращает True если у пользователя есть указанное разрешение, где perm в формате "<app label>.<permission codename>" (см. разрешения). Если User.is_active и is_superuser оба True, этот метод всегда возвращает True.

Если obj передан, этот метод не будет проверять разрешение для модели, а для этого конкретного объекта.

has_perms(perm_list, obj=None)

Возвращает True если у пользователя есть каждое из указанных разрешений, где каждое разрешение в формате "<app label>.<permission codename>". Если User.is_active и is_superuser оба True, этот метод всегда возвращает True.

Если obj передан, этот метод не будет проверять разрешения для модели, а для конкретного объекта.

has_module_perms(package_name)

Возвращает True если у пользователя есть какие-либо разрешения в заданном пакете (метке приложения Django). Если User.is_active и is_superuser оба True, этот метод всегда возвращает True.

PermissionsMixin и ModelBackend

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

Пользователи и прокси-модели

Одним из ограничений пользовательских моделей является то, что установка пользовательской модели нарушит любую прокси-модель, расширяющую User. Прокси-модели должны основываться на конкретном базовом классе; определив пользовательскую модель, вы лишаете Django возможности надёжно определить базовый класс.

Если ваш проект использует прокси-модели, вы должны либо изменить прокси так, чтобы она расширяла модель пользователя, используемую в вашем проекте, либо объединить поведение вашей прокси-модели в ваш User подкласс.

Полный пример

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

Этот код будет находиться в файле models.py для пользовательского приложения аутентификации:

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


class MyUserManager(BaseUserManager):
    def create_user(self, email, date_of_birth, password=None):
        """
        Creates and saves a User with the given email, date of
        birth and password.
        """
        if not email:
            raise ValueError('Users must have an email address')

        user = self.model(
            email=self.normalize_email(email),
            date_of_birth=date_of_birth,
        )

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

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


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

    objects = MyUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['date_of_birth']

    def __str__(self):
        return self.email

    def has_perm(self, perm, obj=None):
        "Does the user have a specific permission?"
        # Simplest possible answer: Yes, always
        return True

    def has_module_perms(self, app_label):
        "Does the user have permissions to view the app `app_label`?"
        # Simplest possible answer: Yes, always
        return True

    @property
    def is_staff(self):
        "Is the user a member of staff?"
        # Simplest possible answer: All admins are staff
        return self.is_admin

Затем, для регистрации этой пользовательской модели с администратором Django, потребуется следующий код в файле admin.py приложения:

from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.exceptions import ValidationError

from customauth.models import MyUser


class UserCreationForm(forms.ModelForm):
    """A form for creating new users. Includes all the required
    fields, plus a repeated password."""
    password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
    password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)

    class Meta:
        model = MyUser
        fields = ('email', 'date_of_birth')

    def clean_password2(self):
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super().save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user


class UserChangeForm(forms.ModelForm):
    """A form for updating users. Includes all the fields on
    the user, but replaces the password field with admin's
    disabled password hash display field.
    """
    password = ReadOnlyPasswordHashField()

    class Meta:
        model = MyUser
        fields = ('email', 'password', 'date_of_birth', 'is_active', 'is_admin')


class UserAdmin(BaseUserAdmin):
    # The forms to add and change user instances
    form = UserChangeForm
    add_form = UserCreationForm

    # The fields to be used in displaying the User model.
    # These override the definitions on the base UserAdmin
    # that reference specific fields on auth.User.
    list_display = ('email', 'date_of_birth', 'is_admin')
    list_filter = ('is_admin',)
    fieldsets = (
        (None, {'fields': ('email', 'password')}),
        ('Personal info', {'fields': ('date_of_birth',)}),
        ('Permissions', {'fields': ('is_admin',)}),
    )
    # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
    # overrides get_fieldsets to use this attribute when creating a user.
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'date_of_birth', 'password1', 'password2'),
        }),
    )
    search_fields = ('email',)
    ordering = ('email',)
    filter_horizontal = ()


# Now register the new UserAdmin...
admin.site.register(MyUser, UserAdmin)
# ... and, since we're not using Django's built-in permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)

Наконец, укажите пользовательскую модель в качестве модели пользователя по умолчанию для вашего проекта, используя настройку AUTH_USER_MODEL в вашем settings.py:

AUTH_USER_MODEL = 'customauth.MyUser'
Изменено в Django 3.2:

В более старых версиях, ReadOnlyPasswordHashField не disabled по умолчанию, и UserChangeForm.clean_password() требуется для возвращения начального значения, независимо от того, что вводит пользователь.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/4.0/topics/auth/customizing/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API