Skip to content
Advertisement

Django: Create a superuser in a data migration

Goal: automatically creating a superuser

I’m trying to create a default user, specifically a superuser, in an early data migration, so whenever my Django application is run in my Docker container, it already has a superuser with which I can access the admin site.

I had already tried different options for creating said superuser, and although I have some functioning ones (based on the command parameter of my docker-compose file), I’ve seen when adding initial data to a Django project, the best practice is to do it through a Data Migration.

My custom user

In my Django project I’ve extended the AbstactBaseUser so I can change the default username field requirement for the email field. My User is as such:

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

Failed attempts

By following the Django documentation here I tried making my superuser in a data migration with the following code, located in a file called 0002_data_superuser in the migrations folder of my app:

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 createdn")

class Migration(migrations.Migration):

    dependencies = [
        ('users', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(generate_superuser)
    ]

When running my docker-compose, however, I run into this error:

AttributeError: 'Manager' object has no attribute 'create_superuser'

I’ve tried debugging by printing the Manager and, indeed, it does not have the create_superuser necessary for this. My next thought was to try to reproduce what create_superuser does myself, but I find it quite impossible given that a lot of methods for managing passwords, hashing, normalizing emails and stuff are not available to me.

I gather from all of this that the problem is that the Manager, for some reason, is not available during migrations, and I’d like to know if there’s a solution to this.

Advertisement

Answer

Reproducing create_user functionality manually

Checking out a bit more the source code of Django I found at there is a way to reproduce the functionality from create_superuser. By importing BaseUserManager and the classmethod make_password from django.contrib.auth.hashers, I came up with this:

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()

which does the trick.

Still, I don’t like it much as a solution given that it requires reeimplementing an already existing Django method that just is not accessible at this point of the program.

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement