Настройка пользовательской модели Django
Не так давно я начал работать над веб-приложением, используя веб-инфраструктуру Django, где хотел подписать пользователей, используя их номер телефона вместо имени пользователя по умолчанию. В процессе выяснения наилучшего способа сделать это я обнаружил много запутанной или устаревшей документации. Эта статья призвана исправить эту ситуацию.
Начало
В этом репозитории Github вы найдете основной, функциональный проект с простой реализацией каждого из трех вариантов. Ссылки на соответствующие файлы для каждой опции внутри репо приведены ниже для удобства пользования. В каждый из них я включил отдельный проект и приложение для каждой опции, все с рабочим шаблоном для аутентификации фронтенда, а также с рабочим методом создания суперпользователя, который можно запустить в командной строке.
Все примеры используют Django 1.11 и Python 3.5.2. Каждый из них добавляет поле zip_code
в модель User
как CharField
, чтобы упростить его, и в первых двух вариантах я делаю поле электронной почты именем пользователя.
Здесь я хотел бы сделать паузу и сказать, что если вы не использовали Django, я рекомендую пройти один или оба превосходных урока от DjangoGirls или Django Docs, чтобы ознакомиться с фреймворком, прежде чем пытаться следовать этому посту. Если вы работали с Django, вы, вероятно, знакомы с моделью User
, готовой функцией, которую вы можете использовать для аутентификации пользователей (например, создавать учетные записи и входить и выходить из приложения) через класс Python, который представляет таблицу в базе данных и поставляется со множеством встроенных полей и методов для извлечения данных и разрешений, о которых вы можете прочитать в официальной документации.
Почему это важно
Заставить пользователей подписаться на ваш продукт часто бывает непросто или свободно, и когда вы, наконец, получите их через форму, вы захотите сделать жизнь максимально удобной для них с информацией, которую вам нужно собрать. В то же время вы не хотите, чтобы ненужные поля оставались пустыми в вашей базе данных.
Если вы используете функции аутентификации Django, у вас есть три варианта настройки вашей модели:
Вариант 1. Создайте свою собственную модель пользователя, создав подкласс Django AbstractBaseUser. Это замечательно, если вам нужна большая гибкость в содержании модели и способах доступа к ней, и вы не возражаете против небольшой дополнительной работы в начале вашего проекта.
Вариант 2. Расширьте существующую модель пользователя своими полями, создав подкласс Django AbstractUser. Этот вариант идеален, если вы хотите сохранить поля по умолчанию в пользовательской модели, и вам не нужно ничего слишком различного с разрешениями, но вы хотите изменить имя пользователя на другое поле и/или добавить поля аутентификации.
Вариант 3. Добавьте поля в существующий класс User с помощью метода OneToOneField. Этот метод не очень помогает, если вы хотите изменить поле имени пользователя, но он позволяет связать модель пользователя с другой моделью для хранения большего количества данных. Он объединяет две модели вместе в базе данных, что было бы полезно, если вы хотите хранить пользовательские данные, которые не имеют ничего общего с аутентификацией, или если некоторые данные пользователя нужны сторонней службе, которую вы хотите оцепить. выключено Это был также рекомендуемый способ настройки пользовательской модели до версии 1.5 Django, поэтому многие существующие приложения используют этот метод.
Из этих трех первые два действительно надежные варианты для большинства случаев использования. В PyCon я разговаривал с несколькими людьми, которые интересовались использованием вариантов 1 или 2, но имели вариант 3 на месте. В этих случаях, поскольку вы вносите фундаментальные изменения в схему базы данных, единственным способом действий является резервное копирование ваших данных и воссоздание ваших моделей и базы данных.
Этот пост не будет касаться реализации сторонних сервисов аутентификации с Django, шагов по миграции существующих моделей или отправки электронных писем пользователям.
Давайте начнем.
Три варианта, шаг за шагом
Вариант 1 - создание подкласса AbstractBaseUser
Это самая высокая сложность, самая высокая гибкость.
Используйте этот подход, если вы:
- Хотите много свободы в настройке модели
- Хотите свести к минимуму количество полей для сбора данных, и вы не думаете, что будете использовать те, которые поставляются по умолчанию
- У вас будет время для написания некоторых основных разрешений, необходимых для работы с моделью разрешений Django.
В папке вашего приложения перейдите в папку models.py и добавьте код в соответствии со строками следующего (с любыми полями, которые вы хотите, конечно!):
Здесь много чего происходит, поэтому давайте немного разберемся с этим.
- Нам нужно добавить параметр
unique = True
в любое поле, которое мы используем в качествеUSERNAME_FIELD
в строке 6. REQUIRED_FIELDS
- это список полей, которые будут обязательны для создания пользователя. Обратите внимание, что включениеUSERNAME_FIELD
здесь приведет к ошибке при попытке запустить приложение. Поскольку я создаю свою форму путем импорта и подклассаUserCreationForm
в моем файлеforms.py
, пароль не нужно добавлять в списокREQUIRED_FIELDS
.
Затем зайдите в файл admin.py
в папке вашего приложения и добавьте следующее, чтобы зарегистрировать вашу модель:
Затем добавьте эту строку, чтобы сказать, что CustomUser
- это то, что вы будете использовать для аутентификации людей.
Затем запустите миграцию базы данных из корня проекта:
python manage.py makemigrations
python manage.py migrate
Тем не менее, с большой гибкостью вы получаете и больше затрат на программирование при настройке!
Если вы попытаетесь запустить приложение с кодом, как сейчас на вашем локальном хосте, оно будет работать, но вы не сможете получить доступ к администратору или добавить пользователя. Причина? Вам необходимо явно определить эту функциональность через менеджера пользователей, и именно здесь появляется наш класс CustomUserManager
.
Чтобы создать эту функцию, вы можете импортировать BaseUserManager
из django.contrib.auth.models
, как показано в строке 2, а затем создать его подкласс, как показано в строке 4, когда создается класс CustomAccountManager
. Вам нужно как минимум определить методы для create_user
, create_superuser
и get_by_natural_key
, иначе вы столкнетесь с ошибками. Если вы хотите создать более конкретные группы разрешений, это то место, где вы должны их определить.
Есть и другие значения, которые вы должны будете установить, чтобы заставить работать пользовательскую модель:
user.is_staff
, как показано в строках 8 и 16 → это контролирует доступ к сайту администратора.user.is_superuser
, как показано в строках 9 и 17 → когда True, пользователь имеет все доступные разрешенияuser.save()
сохраняет данные из формы в базу данных
Метод get_by_natural_key
в строке 21 должен быть настроен на то, какими будут учетные данные id
, фактически имя пользователя, или то, что вы заменяете этим значением — в нашем случае использования это электронная почта.
Но мы еще не закончили! Нам нужно внести некоторые изменения в нашего CustomUser
.
Вы могли заметить в строке 2 на скриншоте выше, мы импортировали что-то под названием PermissionsMixin
. Это действительно полезная модель, которая предоставляет вам методы, необходимые для работы с модулем разрешений Django с минимальными трудностями и страданиями. Убедитесь, что вы импортировали это до AbstractBaseUser
и BaseUserManager
, иначе вы получите ошибки. Затем добавьте его к аргументам вашей модели CustomUser
.
Обратите внимание, что нам также нужно добавить метод get_short_name
, так как Django ожидает, что короткое имя будет доступно для каждого пользователя. Это может быть любое поле, которое вы хотите.
Кроме того, я добавил поле natural_key
, которое возвращает адрес электронной почты - как указано выше, учетные данные, необходимые пользователю для входа в систему, - и простой способ вернуть письмо в виде строки, представляющей учетную запись.
Наконец, реализация объекта CustomAccountManager
в строке 34, который мы создали минуту назад, позволит нам получить доступ к методам, которые мы написали для управления разрешениями и доступа к данным при создании класса CustomAccountManager
.
Не забудьте произвести миграции базы данных после внесения всех этих изменений.
Когда вы создаете суперпользователя (в терминале, python manage.py createsuperuser
), снимок экрана слева - это то, что вы увидите, когда войдете в систему, войдете в систему и создадите пользователя. Обратите внимание, что у нас нет многих стандартных пользовательских полей Django, таких как first_name
, last_name
и т.д.
Вариант 2 — создание подклассов AbstractUser
Это отличный вариант, если вы хотите:
- Используйте поля по умолчанию из модели Django
User
- Управляйть тем, что будет с переменной
username
с минимальными издержками - Пропустить создание собственного менеджера пользователей и используйте методы, встроенные в Django
Однако у этой опции есть некоторые ограничения по настройке имени пользователя, которые мы увидим через минуту.
Первый шаг - написать свою модель.
Обратите внимание, что на этот раз мы импортируем и создаем подклассы AbstractUser
, но нам не нужно поле is_staff
, потому что мы эффективно добавляем модель пользователя по умолчанию, которая уже имеет этот атрибут.
USERNAME_FIELD
сбрасывает имя пользователя по электронной почте, что требуется при входе в систему. У модели уже есть поле электронной почты, но его необходимо добавить и здесь, чтобы добавить параметр unique = True
. Еще одно важное замечание: поле имени пользователя и пароль будут обязательными, поэтому им не нужно входить в список ОБЯЗАТЕЛЬНЫХ ПОЛЕЙ - фактически, если вы сбросите USERNAME_FIELD
, приложение выдаст ошибку.
Добавьте переменную AUTH_USER_MODEL
в файл settings.py
проекта, чтобы приложение знало, как должны выглядеть пользователи.
Зарегистрируйте вашу модель в файле admin.py
проекта, чтобы сделать ее видимой на экране администратора.
На этом этапе не забудьте запустить миграцию:
python manage.py makemigrations
python manage.py migrate
Затем создайте суперпользователя (python manage.py createuperuser
), чтобы протестировать его. Именно здесь вы сталкиваетесь с проблемой, если вы хотите использовать электронную почту в качестве имени пользователя, но не требуете фактического имени пользователя при регистрации: исходный код для модели AbstractUser
требует электронную почту, пароль и имя пользователя.
Это значит, что:
- Если вы не добавите имя пользователя в
REQUIRED_FIELDS
при создании суперпользователя, вы получите ошибку по типуTypeError: create_superuser() missing 1 required positional argument: ‘username’
, потому что модельAbstractUser
ожидает этот аргумент, даже если вы установилиUSERNAME_FIELD = 'email'
- Если вы попытаетесь создать пользователя с помощью формы в браузере, которая не содержит имя пользователя, вы получите ошибку, подобную этой:
NOT NULL constraint failed: abstract_user_sample_customuser.username
Здесь есть несколько вариантов, в зависимости от ваших требований:
- Вместо этого используйте вариант 1 и создайте подкласс
AbstractBaseUser
для своей пользовательской модели. Для этого требуется больше кода, но в некоторых случаях он будет чище - например, вы можете полностью отказаться от имени пользователя, если это не имеет смысла для вашего приложения. - Переопределите параметры поля имени пользователя в вашем файле
models.py
, так же, как вы переопределяете параметры электронной почты, если хотите сохранить поле, но не хотите его запрашивать при создании нового пользователя. Вот простой пример, который удаляет вспомогательный текст, сообщение об ошибке и т.д., которые находятся вне поля, но позволяют этому полю быть пустым или иметь нулевое значение.
Поскольку мы добавили имя пользователя в REQUIRED_FIELDS
в строке 8, оно будет отображаться при создании суперпользователя в командной строке, однако вы можете указать поля в файле forms.py
, так что если вы действительно не хотите, чтобы конечные пользователи добавляли имя пользователя при регистрации, вы можете удалить это полностью, и они все еще могут завершить процесс, хотя и с пустым полем имени пользователя в базе данных.
Обязательно запустите миграцию перед тестированием!
Когда вы войдете в панель администратора и создадите нового пользователя, вы увидите, что — в отличие от варианта 1 — появляются поля Django по умолчанию, которых явно нет в модели, а также добавленное нами поле zip_code
. Обратите внимание, что имя пользователя появляется, но не обязательно.
Вариант 3 — OneToOneField
Эта опция на самом деле не соответствует исходному сценарию использования - для входа пользователя с помощью электронной почты в качестве пароля - но это имеет смысл, если вы хотите хранить пользовательские данные, которые напрямую не связаны с аутентификацией, но связаные с моделью пользователя.
Это довольно просто реализовать. В файле models.py
в вашем приложении вам необходимо импортировать модель User
(показано в строке 2), а затем связать ее с моделью с помощью метода OneToOneField
в строке 5.
В этом случае вам не нужно ничего добавлять в файл settings.py
, потому что вы все еще используете модель Django User
, однако вам все равно нужно зарегистрировать вашу модель в admin.py
, как мы видели в двух других примерах.
Способ, которым модель User
и наша модель CustomUser
связаны, очень понятен в Admin. Когда вы посмотрите на администратора для этой версии, вы увидите, что Users и CustomUsers перечислены отдельно.
Создание пользователя такое же, как обычно, но если вы попытаетесь добавить пользовательского пользователя, администратор запросит у вас имя пользователя записи, к которой вы хотите присоединить нашу пользовательскую модель, и отобразит добавленное нами поле zip_code
.
Вернуться на верх