Spec-Zone.ru › Django 5.1

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

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

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

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

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

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

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

Например, ваша компания может уже иметь установленный LDAP, хранящий имя пользователя и пароль каждого сотрудника. Было бы неудобно как для сетевого администратора, так и для самих пользователей, если бы пользователи имели отдельные учётные записи в LDAP и в приложениях на базе 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 решает этот циклический конфликт, если хотите увидеть, как это обычно делается.)

Приложения повторного использования и AUTH_USER_MODEL

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

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

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

get_user_model() [source]

Вместо прямой ссылки на 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(*, setting, **kwargs):
    if 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 чтобы узнать, был ли он заполнен middleware 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)
acheck_password(raw_password)

Асинхронная версия: acheck_password()

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

Изменено в Django 5.0:

acheck_password() метод был добавлен.

set_unusable_password()

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

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

has_usable_password()

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

get_session_auth_hash()

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

get_session_auth_fallback_hash()

Возвращает HMAC поля пароля, используя SECRET_KEY_FALLBACKS. Используется в get_user().

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.

Расширение стандартной модели User

Если вы полностью довольны моделью пользователя 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 , если у пользователя есть каждое из указанных разрешений, где каждое perm в формате "<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 Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/5.1/topics/auth/customizing/

Spec-Zone.ru

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