Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does initial migration have dependencies?

Without any prior migrations I have added custom User model in core app to enable custom authentication by following the documentation entry Django 2.0 / Customizing authentication in Django

Then I made migrations:

$ python manage.py makemigrations

Migrations for 'core':
  core/migrations/0001_initial.py
    - Create model User

At 0001_initial.py migration class begins with:

class Migration(migrations.Migration):

    initial = True

    dependencies = [
        ('auth', '0009_alter_user_last_name_max_length'),
    ]

I have looked through the official documentation Django 2.0 / Migrations and this file obviously is an initial migrations since it has initial = True. But why does it have a whole chain of dependencies starting with 0009_alter_user_last_name_max_length? Did I made any mistake?

When migrations are ran this is the output:

$ python manage.py migrate  
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, core, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0001_initial... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying core.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying sessions.0001_initial... OK

When custom User model is omitted 0001_initial.py starts with:

class Migration(migrations.Migration):

    initial = True

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

Additionally, I wasn't able to find a good explanation of what migrations.swappable_dependency(settings.AUTH_USER_MODEL) exactly does. What does it do and why is it even called "swappable_dependency"? It would be great if someone could demystify this part for me as well or point in right direction to explore on my own. 🙂

Edit #1

Source code of custom User model.

class UserManager(BaseUserManager):
    def create_user(self, email, password=None):
        if not email:
            raise ValueError('Email not defined.')

        user = self.model(email=self.normalize_email(email))

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password):
        user = self.create_user(
            email,
            password=password,
        )
        user.is_staff = True
        user.save(using=self._db)
        return user


class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    created_at = models.DateTimeField(auto_now_add=True)
    is_staff = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)

    EMAIL_FIELD = 'email'
    USERNAME_FIELD = 'email'

    objects = UserManager()

    def __str__(self):
        return self.email

    def has_perm(self, perm, obj=None):
        return True

    def has_module_perms(self, app_label):
        return True
like image 591
Miro Avatar asked Oct 17 '22 16:10

Miro


1 Answers

This is normal and expected behaviour - you still need the migrations from django.contrib.auth to be applied to your database.

If you look at those migrations they include various other models that are needed by the auth app - things like Permission and Group. The User model has fields that reference these other models, so you need migrations to have set those up first.

Because User is a swappable dependency, Django will ignore the User migrations in the auth app an use yours instead. But the migrations for all the other auth models will be applied from auth.migrations.

It is because of this dependency on the auth migrations that is very difficult to change a custom user model after you've already set up your project:

Due to limitations of Django’s dynamic dependency feature for swappable models, the model referenced by AUTH_USER_MODEL must be created in the first migration of its app (usually called 0001_initial); otherwise, you’ll have dependency issues.

like image 135
solarissmoke Avatar answered Oct 21 '22 05:10

solarissmoke