Referencing external variables in Django data migrations

Viewed 57

When using models in migrations, in Django we can use apps.get_model() to make sure that the migration will use the right "historical" version of the model (as it was when the migration was defined). But how do we deal with "regular" variables (not models) imported from the codebase?

If we import a variable from another module, we will probably face issues in the future. For example:

  1. If someday in the future we delete the variable (because we changed the implementation) this will break migrations. So we won't be able to re-run migrations locally to re-create a database from scratch.
  2. If we modify the variable (e.g. we change the values in a list) this will produce unexpected effects when we run the reverse operation on an existing db.

So the question is: what's the best practice for writing migrations? Should we always hard-code values without importing external variables?


Example

Suppose I want to simply modify the value of a field with a variable that I've defined somewhere in the codebase. For example, I want to turn all normal users into admins. I stored user roles in an enum (UserRoles). One way to write the migration would be this:

from django.db import migrations
from user_roles import UserRoles


def change_user_role(apps, schema_editor):
    User = apps.get_model('users', 'User')
    users = User.objects.filter(role=UserRoles.NORMAL_USER.value)
    for user in users:
        user.role = UserRoles.ADMIN.value

    User.objects.bulk_update(users, ["role"])


def revert_user_role_changes(apps, schema_editor):
    User = apps.get_model('users', 'User')
    users = User.objects.filter(role=UserRoles.ADMIN.value)
    for user in users:
        user.role = UserRoles.NORMAL_USER.value

    User.objects.bulk_update(users, ["role"])


class Migration(migrations.Migration):

    dependencies = [
        ('users', '0015_auto_20220612_0824'),
    ]

    operations = [
        migrations.RunPython(change_user_role, revert_user_role_changes)
    ]

As you see, this will have the issues I mentioned above. I've used the enum example, but this can be applied to every variable referenced inside the migrations.

So the question again: what's the best practice for migrations? Should we always hard-code values without referencing external variables that might change?

0 Answers
Related