Настройка аутентификации в 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, позволяя модулю указать пользовательское поведение авторизации для анонимных пользователей. Это особенно полезно для авторов повторно используемых приложений, которые могут делегировать все вопросы авторизации модулю аутентификации, а не, например, нуждаться в настройках для управления анонимным доступом.
Авторизация для неактивных пользователей
Вы можете использовать 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
В некоторых типах проектов требования к аутентификации, которые модель Django по умолчанию User не всегда подходят. Например, на некоторых сайтах использование адреса электронной почты в качестве маркера идентификации более целесообразно, чем имя пользователя.
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() -
Вместо прямой ссылки на
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.
Импорт
AbstractBaseUserAbstractBaseUserи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, чтобы узнать, был ли он заполнен middlewareAuthenticationMiddleware(представляющий текущего пользователя), вы должны знать, что этот атрибут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 поля пароля. Используется для Отмены сеанса при смене пароля.
-
get_session_auth_fallback_hash() -
Добавлена в Django 4.1.8.
Возвращает 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 Django; однако, если ваша модель пользователя определяет другие поля, вам нужно определить пользовательский менеджер, который расширяет 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') -
Устарело начиная с версии 4.2.
Возвращает случайный пароль заданной длины с использованием заданного набора разрешённых символов. Обратите внимание, что по умолчанию
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. SetPasswordFormPasswordChangeFormAdminPasswordChangeForm
Следующие формы делают предположения о модели пользователя и могут использоваться без изменений, если эти предположения верны:
-
PasswordResetForm: Предполагает, что модель пользователя имеет поле, хранящее адрес электронной почты пользователя с именем, возвращаемымget_email_field_name()(по умолчаниюemail) для идентификации пользователя и логическое поле с именемis_activeдля предотвращения сброса пароля для неактивных пользователей.
И, наконец, следующие формы привязаны к User и требуют переработки или расширения для работы с кастомной моделью пользователя:
Если ваша кастомная модель пользователя является подклассом 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",)
В более старых версиях, UserCreationForm не сохранял поля множественного назначения в форме для кастомной модели пользователя.
Пользователи кастомной модели и 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.
-
Пользователи с кастомной моделью и модели-прокси
Один из ограничений кастомных моделей пользователей заключается в том, что установка кастомной модели пользователя нарушит любую модель-прокси, расширяющую 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/4.2/topics/auth/customizing/