Search code examples
djangodjango-migrations

Django data migration fails unless it's run separately


I've run into this a couple other times and can't figure out why it happens. When I run the migrations all together through ./manage.py migrate then the last migration (a data migration) fails. The solution is to run the data migration on it's own after the other migrations have been completed. How can I run them all automatically with no errors?

I have a series of migrations:

  1. fulfillment/0001.py
  2. order/0041.py (dependency: fulfillment/0001.py)
  3. order/0042.py
  4. order/0043.py

I followed this RealPython article to move a model to a new app which which works perfectly and is covered by migrations #1 to #3. Migration #3 also adds a GenericForeignKey field. Migration #4 is a data migration that simply populates the GenericForeignKey field from the existing ForeignKey field.

from django.db import migrations, models

def copy_to_generic_fk(apps, schema_editor):
    ContentType = apps.get_model('contenttypes.ContentType')
    Order = apps.get_model('order.Order')
    pickup_point_type = ContentType.objects.get(
        app_label='fulfillment',
        model='pickuppoint'
    )
    Order.objects.filter(pickup_point__isnull=False).update(
        content_type=pickup_point_type,
        object_id=models.F('pickup_point_id')
    )

class Migration(migrations.Migration):

    dependencies = [
        ('order', '0042'),
    ]

    operations = [
        migrations.RunPython(copy_to_generic_fk, reverse_code=migrations.RunPython.noop)
    ]

Running the sequence together I get an error:

fake.DoesNotExist: ContentType matching query does not exist.

If I run the migration to #3 then run #4 by itself everything works properly. How can I get them to run in sequence with no errors?


Solution

  • Instead of getting the ContentType through .get() you have to retrieve the model through the apps argument then use get_for_model().

    def copy_to_generic_fk(apps, schema_editor):
        ContentType = apps.get_model('contenttypes', 'ContentType')
        PickupPoint = apps.get_model('fulfillment', 'pickuppoint')
        pickup_point_type = ContentType.objects.get_for_model(PickupPoint)
        ...