DragonBobZ
DragonBobZ

Reputation: 2444

Django TestCase omitting some migrations

For some reason, Django (3.0.7,8) is not running one of my migrations users.0002_customuser_is_service_account when executing a TestCase. It's not failing, it just seems to ignore it. This migration has been applied successfully to my development database, and under normal circumstances, the migrate command seems to detect it without issue.

Revert to Initial:

python manage.py migrate users 0001
Operations to perform:
  Target specific migration: 0001_initial, from users
Running migrations:
  Rendering model states... DONE
  Unapplying users.0002_customuser_is_service_account... OK

Re-apply by auto-detecting migration

 python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, csv_management, reversion, sessions, softdelete, tasks, trades, trades_portal, users
Running migrations:
  Applying users.0002_customuser_is_service_account... OK

Migrations in Development Database: enter image description here

But when I attempt to run this simple TestCase, it complains that it cannot find a column.

from django.test import TestCase
from users.models import CustomUser

class TestExists(TestCase):
    def test_thing(self):
        CustomUser.objects.all()
python manage.py test trades_portal --no-input

django.db.utils.ProgrammingError: column "is_service_account" of relation "users_customuser" does not exist
LINE 1: ... "email", "is_staff", "is_active", "date_joined", "is_servic...

If I look at the test database after the test fails, sure enough, that column does not exist: enter image description here

And looking in the migrations, the migration which adds that column was never run: enter image description here

Here is the migration:

# Generated by Django 3.0.7 on 2020-06-12 14:50

from django.db import migrations, models


class Migration(migrations.Migration):

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

    operations = [
        migrations.AddField(
            model_name='customuser',
            name='is_service_account',
            field=models.BooleanField(default=False, help_text='Designation for whether this account receives certain actions, such as receiving emails for all users.', verbose_name='Is Service Account'),
        ),
    ]

The thing is, I tried to re-create this problem in another project, but I could not. The migrations are all applied and the test runs successfully. I am at a complete loss for what is happening here.

Upvotes: 0

Views: 300

Answers (1)

DragonBobZ
DragonBobZ

Reputation: 2444

Turns out that

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

specified as part of a migration does not ensure that all custom user model migrations are run before this migration is run. It only seems to ensure that custom_user.migrations.0001_initial is run.

Because of this, when a migration actually attempts to fetch or create such a custom user by using the custom user model as one of its operations, you are not guaranteed that all user model migrations have been run. Such an example is below.

def add_initial_auth(apps, schema_editor):

    Group = apps.get_model('auth', 'Group')
    User = get_user_model()
    initial_groups = INITIAL_GROUPS

    existing_groups = list(Group.objects.filter(name__in=initial_groups).values_list('name', flat=True))

    Group.objects.bulk_create(
        [Group(name=group_name) for group_name in initial_groups if group_name not in existing_groups]
    )

    _ = User.objects.create_superuser(username=settings.INITIAL_ADMIN_USERNAME,
                                      password=settings.INITIAL_ADMIN_PASSWORD,
                                      email=settings.INITIAL_ADMIN_EMAIL)


class Migration(migrations.Migration):

    initial = True

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

    operations = [migrations.RunPython(add_initial_auth)]

The solution unfortunately is that you must specify the latest migration as a dependency:

dependencies = [
    migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ('users', '0002_customuser_is_service_account')
]

Upvotes: 1

Related Questions