Расширяем модуль аутентификации 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 не будет пытаться проверить бэкенды, следующие по списку далее.
Примечание
Once a user has authenticated, Django stores which backend was used to
authenticate the user in the user’s session, and re-uses the same backend
for the duration of that session whenever access to the currently
authenticated user is needed. This effectively means that authentication
sources are cached on a per-session basis, so if you change
AUTHENTICATION_BACKENDS, you’ll need to clear out session data if
you need to force users to re-authenticate using different methods. A
simple way to do that is to execute 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() (который передает его на серверную часть).
The Django admin is tightly coupled to the Django User object. The best way to deal with this is to create a Django User
object for each user that exists for your backend (e.g., in your LDAP
directory, your external SQL database, etc.) You can either write a script to
do this in advance, or your authenticate method can do it the first time a
user logs in.
Here’s an example backend that authenticates against a username and password
variable defined in your settings.py file and creates a Django User
object the first time a user authenticates:
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
Создаем пользовательские права доступа¶
Что бы создать пользовательские права доступа для вашей моделинеобходимо определить их в переменной 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)
Assuming an existing Employee Fred Smith who has both a User and Employee model, you can access the related information using Django’s standard related model conventions:
>>> 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'
This dotted pair describes the name of the Django app (which must be in your
INSTALLED_APPS), and the name of the Django model that you wish to
use as your user model.
Использование пользовательской модели пользователя при запуске проекта¶
If you’re starting a new project, it’s highly recommended to set up a custom
user model, even if the default User model
is sufficient for you. This model behaves identically to the default user
model, but you’ll be able to customize it in the future if the need arises:
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)
Переход на пользовательскую модель пользователя в середине проекта¶
Changing AUTH_USER_MODEL after you’ve created database tables is
significantly more difficult since it affects foreign keys and many-to-many
relationships, for example.
Изменение этой настройки после того как вы уже создали ваши таблицыне отразится в 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(**kwargs): if kwargs['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¶
A string describing the name of the field on the user model that is used as the unique identifier. This will usually be a username of some kind, but it can also be an email address, or any other unique identifier. The field must be unique (i.e., have
unique=Trueset in its definition), unless you use a custom authentication backend that can support non-unique usernames.В примере ниже поле
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, таких как создание пользователя через панель администратора.New in Django 3.0:REQUIRED_FIELDSnow supportsManyToManyFields without a custom through model. Since there is no way to pass model instances during thecreatesuperuserprompt, expect the user to enter IDs of existing instances of the class to which the model is related.Пример ниже показывает как можно задать два обязательных поля –дата рождения и рост:
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)¶
Возвращает
Trueесли пароль указан верно (вычисляет хэш исравнивает его с хэшем хранимым в базе данных).
- 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 (хэш) пароля. Используется для переопределения сессиипосле смены пароля.
Changed in Django 3.1:The hashing algorithm was changed to the SHA-256.
Работает с любым классом унаследованным от 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.
- make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')¶
Returns a random password with the given length and given string of allowed characters. Note that the default value of
allowed_charsdoesn’t contain letters that can cause user confusion, including:i,l,I, and1(lowercase letter i, lowercase letter L, uppercase letter i, and the number one)o,O, and0(lowercase letter o, uppercase letter o, and zero)
Расширяем встроенную модель User¶
If you’re entirely happy with Django’s User
model, but you want to add some additional profile information, you could
subclass django.contrib.auth.models.AbstractUser and add your custom
profile fields, although we’d recommend a separate model as described in the
«Model design considerations» note of Определение пользовательской модели.
AbstractUser provides the full implementation of the default
User as an abstract model.
Встроенные формы авторизации¶
Встроенные в 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)¶
- New in Django 3.0.
Возвращает набор прав доступа групп в которых состоит пользователь.
Если передается
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
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)
И, на конец, указать вашу пользотельскую модель в качестве моделипо умолчанию в файле settings.py (AUTH_USER_MODEL):
AUTH_USER_MODEL = 'customauth.MyUser'