Django: Создание суперпользователя в миграции данных
Цель: автоматическое создание суперпользователя
Я пытаюсь создать пользователя по умолчанию, а именно суперпользователя, в ранней миграции данных, так что всякий раз, когда мое приложение Django запускается в моем контейнере Docker, оно уже имеет суперпользователя, с которым я могу получить доступ к сайту администратора.
Я уже пробовал различные варианты создания указанного суперпользователя, и хотя у меня есть несколько работающих (на основе command
параметра моего файла docker-compose), я видел, что при добавлении начальных данных в проект Django, лучшая практика - делать это через Data Migration.
Мой пользовательский пользователь
В моем проекте Django я расширил AbstactBaseUser
, чтобы я мог изменить требование поля имени пользователя по умолчанию для поля электронной почты. Мой User
выглядит следующим образом:
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
# Implementation...
def create_superuser(self, email, password, **extra_fields):
# Implementation...
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(
verbose_name="email address",
max_length=255,
unique=True,
)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
def __str__(self):
return self.email
Неудачные попытки
Следуя документации Django здесь я попробовал сделать суперпользователя в миграции данных с помощью следующего кода, расположенного в файле под названием 0002_data_superuser в папке migrations моего приложения:
def generate_superuser(apps, schema_editor):
User = apps.get_model("users.User")
User.objects.create_superuser(
email=settings.DJANGO_SUPERUSER_EMAIL,
password=settings.DJANGO_SUPERUSER_PASSWORD,
)
print("\nInitial superuser created\n")
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.RunPython(generate_superuser)
]
При запуске моего docker-compose, однако, я сталкиваюсь с этой ошибкой:
AttributeError: 'Manager' object has no attribute 'create_superuser'
Я попробовал отлаживать, распечатывая Менеджер, и, действительно, в нем нет create_superuser
, необходимого для этого. Следующей моей мыслью было попытаться воспроизвести то, что делает create_superuser
, самостоятельно, но я нахожу это совершенно невозможным, учитывая, что многие методы управления паролями, хэширования, нормализации электронной почты и прочего мне недоступны.
Из всего этого я понял, что проблема в том, что менеджер по какой-то причине недоступен во время миграции, и я хотел бы узнать, есть ли решение этой проблемы.
Добавьте use_in_migrations = True
в ваш пользовательский менеджер, чтобы сделать его доступным во время миграций
class UserManager(BaseUserManager):
use_in_migrations = True
...
Тогда вы должны иметь возможность использовать User.objects.create_superuser
в миграции данных
Однако будьте осторожны, есть некоторые последствия для этого здесь в документации
Восстановление функциональности create_user
вручную
Проверив немного больше исходного кода Django, я обнаружил, что там есть способ воспроизвести функциональность из create_superuser
. Импортировав BaseUserManager
и метод класса make_password
из django.contrib.auth.hashers
, я получил следующее:
from django.contrib.auth.models import BaseUserManager
from django.contrib.auth.hashers import make_password
def generate_superuser(apps, schema_editor):
User = apps.get_model("users.User")
email = settings.DJANGO_SUPERUSER_EMAIL
password = settings.DJANGO_SUPERUSER_PASSWORD
user = User()
user.email = BaseUserManager.normalize_email(email)
user.password = make_password(password)
user.is_staff = True
user.is_superuser = True
user.save()
который делает трюк.
Тем не менее, мне не очень нравится это решение, учитывая, что оно требует повторной реализации уже существующего метода Django, который просто недоступен на данном этапе программы.
В документации Django есть четкое руководство по выполнению чего-то подобного Data Migrations.
Сначала создайте пустую миграцию в вашем пользовательском приложении
python manage.py makemigrations --empty yourappname
В результате будет создан пустой файл миграции, затем
import logging
import environ
from django.db import migrations
logger = logging.getLogger(__name__)
def generate_superuser(apps, schema_editor):
from django.contrib.auth import get_user_model
env = environ.Env()
USERNAME = env.str("ADMIN_USERNAME")
PASSWORD = env.str("ADMIN_PASSWORD")
EMAIL = env.str("ADMIN_EMAIL")
user = get_user_model()
if not user.objects.filter(username=USERNAME, email=EMAIL).exists():
logger.info("Creating new superuser")
admin = user.objects.create_superuser(
username=USERNAME, password=PASSWORD, email=EMAIL
)
admin.save()
else:
logger.info("Superuser already created!")
class Migration(migrations.Migration):
dependencies = [("users", "0009_user_full_name")]
operations = [migrations.RunPython(generate_superuser)]
Этот рабочий процесс хорошо работает с Docker, после запуска python migrate будет создан новый пользователь.