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 будет создан новый пользователь.

Вернуться на верх