Расширяем модуль аутентификации Django¶
Система аутентификации, входящая в состав Django, отлично подойдёт для решения многих типичных задач, однако вам может не хватить функциональности, предоставляемой «из коробки». Для того, чтобы настроить аутентификацию под требования своего проекта сначала необходимо разобраться какие части этой системы могут быть расширены или заменены. В этом документе мы детально рассмотрим возможности настройки системы аутентификации.
Бэкенды аутентификации обеспечивают расширяемую систему для аутентификации в разных сервисах, в дополнение к стандартной для Django системе аутентификации через модель User.
Вы можете назначить вашим моделям собственные права доступа, которые могут быть проверены системой аутентификации Django.
Вы можете расширить стандартную модель User или полностью заменить эту модель.
Другие сервисы аутентификации¶
Бывают случаи когда вам необходимо подключиться к сторонним сервисам аутентификации, использующим альтернативные источники имён пользователей, паролей или иные методы аутентификации.
Например, в вашей компании используется система LDAP для хранения логинов и паролей для каждого сотрудника. Иметь разные аккаунты для LDAP и Django приложений не очень удобно, как для пользователей, так и для администраторов сети.
В такой ситуации возможно интегрировать систему аутентификации Django с другой системой аутентификации. Вы можете переопределить стандартную систему аутентификации Django или объединить её с другими системами.
Обратитесь к справочнику по бренду аутентификации для информации по бэкендам аутентификации, поставляемым с Django.
Бэкенды аутентификации¶
Система аутентификации Django поддерживает список бэкендов, которые она применяет в свой работе. Когда кто-то вызывает django.contrib.auth.authenticate() – в соответствии с документом Как авторизовать пользователя – Django пробует авторизовать пользователя с помощью бекэндов из списка. Если первый бэкенд не подошёл, Django пробует использовать второй и так далее пока не кончится список.
Список бэкендов аутентификации определён в параметре AUTHENTICATION_BACKENDS файла конфигурации в виде кортежа путей к классам 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(**credentials), а так же ряд необязательных прав, относящихся к методам аутентификации.
Метод get_user принимает параметр user_id, который может быть как именем пользователя, так и ID базы данных или чем-либо ещё, но передаваемое значение обязательно должно быть первичным ключом модели User, и возвращает объект User.
Метод authenticate принимает учётные данные в виде именованных аргументов. В основном, это будет выглядеть так:
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 должен проверить переданные ему учётные данные и вернуть объект User, соответствующий этим данным в случае их корректности. В другом случае он должен вернуть None.
request — это HttpRequest и может быть None, если он не был предоставлен authenticate() (который передает его на серверную часть).
Администратор Django тесно связан с объектом пользователя Django <user-objects>. Например, чтобы пользователь мог получить доступ к администратору, User.is_staff и User.is_active должны иметь значение True (подробности см. в разделе AdminSite.has_permission()).
Лучший способ справиться с этим — создать объект «Пользователь» Django для каждого пользователя, существующего в вашем бэкэнде (например, в вашем каталоге LDAP, внешней базе данных SQL и т. д.). Вы можете либо заранее написать сценарий, чтобы сделать это, либо ваш метод аутентификации может сделать это при первом входе пользователя в систему.
Вот пример серверной части, которая выполняет аутентификацию по переменной имени пользователя и пароля, определенной в вашем файле settings.py, и создает объект Django User при первой аутентификации пользователя. В этом примере созданный объект «Пользователь» Django является суперпользователем, который будет иметь полный доступ к администратору:
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) # is_active defaults to True.
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
Создаем пользовательские права доступа¶
Что бы создать пользовательские права доступа для вашей моделинеобходимо определить их в переменной permissions :ref:`атрибуте Meta<meta-options>`вашей модели.
В примере ниже описывается как создать пользовательсике права доступадля модели 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. Вашему приложению будетнеобходимо проверять эти значения каждый раз когда пользовательпытается получить доступ к функциям приложения (просмотр заданий,изменения статуса заданий, закрытие заданий). В продолжении выше описаногопримера, так вы можете проверить права пользователя для просмотра заданий:
user.has_perm("app.close_task")
Расширяем модель User¶
Существуюет два способа расширения стандартной моделиclass:~django.contrib.auth.models.User без замены модели на вашусобственную. Если вам нужно изменить только поведение моделибез измения данных вы можете создать proxy модель <proxy-models>`на основе модели :class:`~django.contrib.auth.models.User. С помощью модели proxyвы можете,добавить, добавить пользовательские менеджеры или методы сохранив стандартныефункции.
Если вы хотите хранить ддополнительную информацию относящуююся к модели``User`` вы можете использовать связь один к одному с полямимодели хранящей эту ифнормацию. Эту связанную модель часто называют профайлпользователя, так как она хранит информацию не относящююся к аутентификациипользователей. Например, вы можете создать модель 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)
Предполагая, что у Фреда Смита есть сотрудник, у которого есть модель пользователя и сотрудника, вы можете получить доступ к связанной информации, используя стандартные соглашения о связанных моделях Django:
>>> u = User.objects.get(username="fsmith")
>>> freds_department = u.employee.department
Что бы добавить поля из модели профайла в интерфейс администраторанужно определить InlineModelAdmin в файле``admin.py`` вашего приложения (для этого примера используется:class:~django.contrib.admin.StackedInline), добавить его к классу ``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. От других моделей ее отличаеттолько наличие свзяи один к одному с моделью User. Как правило записив этой модели не создаются автоматичски вместе с созданием пользователя,но вы можете воспользоваться методом :attr:`django.db.models.signals.post_save`для создания или обновления записей в профайле по мере необходимости.
Заметим, что использование связаных моделей приводит к увеличениюзапросов к базе данных и дополнительной нагрузке на нее, и, в зависиомтисот ваших потребностей, замена модели User на вашу собственную модельс нужными вам полями может быть лучшим выбором.
Заменяем стандартную модель User¶
Для некоторых проектов возможностей встроенной в Django модели:class:~django.contrib.auth.models.User будет недостаточно. Например,вам необходимо использовать в качестве идентификатора пользоавтеляemail адрес вместо имени пользователя.
Вы можете переписать стандартную модель User указав в настройках приложения AUTH_USER_MODEL ссылка на вашу модель:
AUTH_USER_MODEL = "myapp.MyUser"
Эта пунктирная пара описывает label приложения Django (которое должно быть в вашем INSTALLED_APPS) и имя модели Django, которую вы хотите использовать в качестве пользовательской модели.
Использование пользовательской модели пользователя при запуске проекта¶
Если вы начинаете новый проект, вы можете настроить собственную модель пользователя, которая будет вести себя идентично модели пользователя по умолчанию, создав подкласс AbstractUser:
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
Не забудьте указать на него AUTH_USER_MODEL. Сделайте это перед созданием каких-либо миграций или первым запуском «manage.pymigration».
Кроме того, зарегистрируйте модель в файле 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 после создания таблиц базы данных возможно, но может быть сложным, поскольку оно влияет, например, на внешние ключи и отношения «многие ко многим».
Изменение этой настройки после того как вы уже создали ваши таблицыне отразится в makemigrations и вам нужно будет вручную правитьсхему вашей БД, переносить данные из старой таблицы пользователей и,возможно, вручную применять некторые миграции.
Из-за ограничей в динамических зависимостях Django вам необходимоубедиться что заменяемая модель указанная в :setting:`AUTH_USER_MODEL`была создано в вашей первой миграции (обчно она называется 0001_initial).В противном случае у вас будут проблемы с зависимотями.
Кроме того вы можете столкнуться с CircularDependencyError когда запуститемиграцию, Django не сможет самостоятельно выйти из бесконечного цикла вызванногоавтоматическими зависимостями. Если вы видите эту ошибку вам необходимо определитьмодель ссылающуюся на старую модель User и исправить это в следующей миграции.Вы можете создать две обычные модели с внешними ключами ссылающимися друг на другаи, запустив makemigrations, увидеть что происходит когда появляются циклические зависимости.
Многоразовые приложения и AUTH_USER_MODEL¶
Приложения многократного использования не должны реализовывать пользовательскую модель пользователя. В проекте может использоваться множество приложений, и два приложения многократного использования, в которых реализована пользовательская модель пользователя, не могут использоваться вместе. Если вам нужно хранить информацию о каждом пользователе в вашем приложении, используйте ForeignKey или OneToOneField для settings.AUTH_USER_MODEL, как описано ниже.
Ссылка на модель User¶
Если вы ссылаетесь на модель на модель User`напрямую (например испльзую внешние ключи), ваш код не будет работать впроектах где стандартная модель заменена на другую в настройке:setting:`AUTH_USER_MODEL.
- get_user_model()¶
Вместо того что бы ссылаться на модель
get_user_model()`.Этот метод возвращает действующею модель – указаную в настройках приложенииили стандартную модельUser, в случае если не изменялась.Когда вы определяете в вашей модели пользователей внешние ключи илиотношения многие ко многим вы должны указывать параметр:setting: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 импортирует модели.Если ваше приложение тестируется с использованием нескольких пользовательских моделей, например, с использованием
@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 ожидает, что пользовательские модели будут соответствовать.
Ваша модель хотя бы одно уникальное поле для идентификации.Это может быть имя пользователя, имейл адрес или любой другойуникальный атрибут.
Простейший способ построить модель совместимую с моделью User этонаследовать модель AbstractBaseUser.:class:~django.contrib.auth.models.AbstractBaseUser обеспечивает базовуюреализацию модели User, в том числе хэширование паролей и сброс пароляпосредством токенов.
- 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вы можете задать любые обязательные полявашей моделиUserкромеUSERNAME_FIELDиpassword. Эти поляявляются обязательными по умолчанию.
- is_active¶
Логические атрибут указывающий является ли пользователь «активным».По умолчанию класс
AbstractBaseUserустанавливает его вTrue. Выможете изменить это в вашем бекэнде аутентификации. Для подробностейсмотри документатрибут is_active во встроеное модели ``User``
- get_full_name()¶
Необязательный. Более длинный формальный идентификатор пользователя, например его полное имя. Если реализовано, оно отображается рядом с именем пользователя в истории объекта в
django.contrib.admin.
- get_short_name()¶
Необязательный. Короткий неофициальный идентификатор пользователя, например его имя. Если это реализовано, это заменяет имя пользователя в приветствии пользователя в заголовке
django.contrib.admin.
Импорт
AbstractBaseUserAbstractBaseUserиBaseUserManagerможно импортировать изdjango.contrib.auth.base_user, поэтому их можно импортировать без включенияdjango.contrib.authвINSTALLED_APPS.
Следующие методы доступны из любого класса наследующего:class:~django.contrib.auth.models.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)¶
Применяет нормализацию Юникода NFKC к именам пользователей, чтобы визуально идентичные символы с разными кодовыми точками Юникода считались идентичными.
- is_authenticated¶
Атрибут только для чтения, который всегда имеет значение True (в отличие от AnonymousUser.is_authenticated, который всегда имеет значение False). Это способ узнать, прошел ли пользователь аутентификацию. Это не подразумевает никаких разрешений и не проверяет, активен ли пользователь или имеет ли действительный сеанс. Несмотря на то, что обычно вы проверяете этот атрибут в
request.user, чтобы узнать, был ли он заполненAuthenticationMiddleware(представляющим текущего вошедшего в систему пользователя), вы должны знать, что этот атрибут имеет значениеTrueдля любого экземпляраUser.
- is_anonymous¶
Всегда возвращает
False. Это способ дифференцировать от объектов:class:~django.contrib.auth.models.AnonymousUser. Обычно предпочтительнееиспользоватьis_authenticated()метод.
- set_password(raw_password)¶
Меняет пароль пользователя на переданный в параметре педварительнохэшировав его. Объект
AbstractBaseUserне сохраняется.Если используется метод:meth:~django.contrib.auth.models.AbstractBaseUser.set_unusable_password() и
raw_passwordравенNoneпользователь будетпомечен как не использующий пароль.
- check_password(raw_password)¶
- acheck_password(raw_password)¶
Асинхронная версия:
acheck_password()Возвращает
Trueесли пароль указан верно (вычисляет хэш исравнивает его с хэшем хранимым в базе данных).Changed in Django 5.0:Добавлен метод acheck_password().
- set_unusable_password()¶
Помечает пользователя как не использующего пароля. Но это не тожесамое что указывать пустую строку в качестве пароля. Если уставнолено тометод
check_password()`никогда не вернет ``True`(). Объект :class:`~django.contrib.auth.models.AbstractBaseUser`не сохраняется.Вам может это понадобиться если вы используете внешний источникданных для аутентифифкации, например, директорю LDAP.
- has_usable_password()¶
Вернет
Falseесли для пользователя бул вызван метод:meth:~django.contrib.auth.models.AbstractBaseUser.set_unusable_password().
- get_session_auth_hash()¶
Вернет HMAC (хэш) пароля. Используется для переопределения сессиипосле смены пароля.
- get_session_auth_fallback_hash()¶
Возвращает HMAC поля пароля, используя
SECRET_KEY_FALLBACKS. Используетсяget_user().
Работает с любым классом унаследованным от AbstractBaseUser
- class models.AbstractUser¶
- clean()¶
Нормализует электронную почту, вызывая
BaseUserManager.normalize_email(). Если вы переопределите этот метод, обязательно вызовитеsuper(), чтобы сохранить нормализацию.
Определение пользовательской модели¶
Вам так же необходимо определить собственный менеджер пользователейвашей модели User. Если ваше модель User определяет поля username,``email``, is_staff, is_active, is_superuser, last_login и date_joined так жеи в стандартной модели User вы можете использовать стандартный UserManager Django. Однако, если вы определили другиеполя в вашей модели вам необходим ваш собственный менеджер пользователйкоторый будет унаследован от :class:`~django.contrib.auth.models.BaseUserManager`и определять два обязательных метода:
- class models.CustomUserManager¶
- create_user(username_field, password=None, **other_fields)¶
Метод
create_user()должен принимать вкачестве параметровимя пользователя плюс другие обязательные поля. Например,если ваша модель пользователей используетemailв качествелогина с обязательным указанием даты рождения то ваш метод``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в качествелогина с обязательным указанием даты рождения то ваш метод``create_superuser()`` должен быть определен как:def create_superuser(self, email, date_of_birth, password=None): # create superuser here ...
Атрибут USERNAME_FIELD теперь поддеоживает внешние ключи.В связи с этим возникает проблема. При выполнении команды из:djadmin:createsuperuser из командной строки вы неможете передатьобъект связанного объекта, по этому вы можете просто указать простоID записи в БД (to_field указываетна ID по умолчанию).
BaseUserManager предоставляет следующие методы:
- class models.BaseUserManager¶
- classmethod normalize_email(email)¶
Это
classmethodкоторый используется для нормализации электронногоадреса путем преобразования доменного имени в нижний регистр.
- get_by_natural_key(username)¶
Получает экземпляр пользователя, использую содержимое поля,указаного в
USERNAME_FIELD.
Расширяем встроенную модель User¶
Если вас полностью устраивает модель Django User, но вы хотите добавить некоторую дополнительную информацию о профиле, вы можете создать подкласс django.contrib.auth.models.AbstractUser и добавить свои собственные поля профиля, хотя мы рекомендуем использовать отдельную модель, как описано в Определение пользовательской модели. AbstractUser обеспечивает полную реализацию стандартного User как абстрактную модель.
Встроенные формы авторизации¶
Встроенные в Django формы и:ref:представления <built-in-auth-views> делают определенные предположенияо модели User с которой они работают.
Следующие методы доступны из любого класса наследующего:class:~django.contrib.auth.models.AbstractBaseUser:
AuthenticationForm: использует поле имени пользователя, указанное вUSERNAME_FIELD.
В следующих формах сделаны предположения о пользовательской модели, и их можно использовать без изменений, если эти предположения выполняются:
Предполагается что модель пользователя имеет поле с именем
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",)
Пользовательская модель и 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.contrib.auth.admin.UserAdmin класс Django.Однако, если вы наследуеете 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"]}),)
См. полный пример <custom-users-admin-full-example> для получения более подробной информации.
Пользовательские права доступа¶
Что бы упростить подключения системы прав доступа фреймворка в вашпользовательский класс Django имеет класс PermissionsMixin. Это абстрактная модель которую вы можете включить в иерархиюклассов вашей пользовательской модели, предоставляющий вам доступ ко всемметодам и полям базы данных необходиых для работы модуля прав доступа.
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>"(дляподробностей смотри документ – права доступа).Если пользователь не активен будет всегда возвращатьFalse.Если передан
obj, этот метод проверит права доступа толькодля указанного объекта.
- has_perms(perm_list, obj=None)¶
Возвращает
Trueесли пользователь имеет все перечисленные пара доступапереданные в формате"<app label>.<permission codename>". Если пользователь не активен метод вернетFalse.Если передан
obj, этот метод проверит права доступа толькодля указанного объекта.
- has_module_perms(package_name)¶
Вернет
Trueесли пользователь имеет какие либо права доступа дляуказанного пакета (приложения Django). ВернетFalseеслипользователь не активен.
Пользовательская модель User и Proxy модели¶
Одним из ограничений пользовательской модели User является то, что она сломает любые Proxy модели расширяющиеестандартную модель. Proxy модели должны основываться на конкретомбазовом классе. Определяя пользовательскую модель User вы лишаеетеDjango возможности конкрентно определить базовый класс.
Если в своем проекте вы используете Proxy модели вы должны либомодифицировать ее что бы она расширяла модель User, котораяиспользуется в ваше проекте, либо определить функции выполняемыеProxy моделями непосредственно вашей пользовательской модели.
Полный пример¶
Здесь вы можете посмотреть пример админ-совместимого приложенияуправления пользователями. Пользовательская модельиспользует, в качествеимени пользователя, поле admin и обязательное поле даты рождения. Такжеона не предоставлет никаких пользовательских прав, за исключением простого флага``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)
И, на конец, указать вашу пользотельскую модель в качестве моделипо умолчанию в файле settings.py (AUTH_USER_MODEL):
AUTH_USER_MODEL = "customauth.MyUser"