Spec-Zone.ru › Django 2.2

Настройка аутентификации в 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 и учетные данные в качестве ключевых аргументов. В большинстве случаев он будет выглядеть так:

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

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

class MyBackend:
    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.hashers import check_password
from django.contrib.auth.models import User

class SettingsBackend:
    """
    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_group_permissions(), get_all_permissions(), has_perm() и has_module_perms()) любому бэкенду аутентификации, который реализует эти функции.

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

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

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

class SettingsBackend:
    ...
    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 в большинстве случаев. Если вы хотите предоставить настраиваемое поведение только для части API бэкэнда, вы можете воспользоваться наследованием Python и подклассом ModelBackend вместо реализации полного API в пользовательском бэкенде.

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

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

Система разрешений 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)

Предполагая, что существует существующий сотрудник Fred Smith, у которого есть как модель 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'

Эта точка с парой описывает имя приложения 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 была изменена на другую модель пользователя.

END_OF_DOCUMENT_MARKER
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(**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, вы можете просто использовать менеджер 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, **other_fields)

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

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

В отличие от create_user(), create_superuser() обязательно требует, чтобы вызывающий предоставил пароль.

Для 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_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):
        """
        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 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 forms.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
    password hash display field.
    """
    password = ReadOnlyPasswordHashField()

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

    def clean_password(self):
        # Regardless of what the user provides, return the initial value.
        # This is done here, rather than on the field, because the
        # field does not have access to the initial value
        return self.initial["password"]


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/2.2/topics/auth/customizing/

Spec-Zone.ru

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