Django tests fails when using a specific model in a migration file
Asked Answered
B

3

5

I've manually created a data migration file for a specific Django 1.11 app:

from __future__ import unicode_literals
from django.db import migrations, models

def set_item_things(apps, schema_editor):
    MyModel = apps.get_model('my_app', 'MyModel')
    # NOTE: if I remove this line then the tests will work
    MyOtherModel = apps.get_model('my_other_app', 'MyOtherModel')

    for item in MyModel.objects.all():
        # NOTE: if I remove this line then the tests will work
        thingy = MyOtherModel.get(example_field=item.color) 
        item.other_thing = thingy
        item.save()

class Migration(migrations.Migration):
    dependencies = [
        ('contracts', '0014_my_previous_migration'),
    ]

    operations = [
        migrations.RunPython(set_item_things),
    ]

When I run python manage.py migrate everything works as expected.
But whenever I run my test using pytest I get this:

test setup failed
self = <django.db.migrations.state.StateApps object at 0x10714b2b0>
app_label = 'my_other_app'

    def get_app_config(self, app_label):
        """
            Imports applications and returns an app config for the given label.

            Raises LookupError if no application exists with this label.
            """
        self.check_apps_ready()
        try:
>           return self.app_configs[app_label]
E           KeyError: 'my_other_app'

So it looks like the app config is not properly configured, and that's already weird because the migrate command ran smoothly.

Anyway: this is the content of my_other_app/apps.py:

from django.apps import AppConfig

class MyOtherAppConfig(AppConfig):
    name = 'my_other_app'

And basically is very similar to all the others apps.py sitting in the other apps directories, except of course for the name.

So I think the configuration should be correct but for whatever reasons my tests won't run.

The only fix is to remove any reference to my_other_app from the migration file.

I've already tried to add this to the my_other_apps/__init__.py:

default_app_config = 'my_other_apps.apps.MyOtherAppConfig'

but nothing changes.

I've already tried to see if there are circular dependencies inside my_other_apps/models.py but it doesn't seems the case.

What am I missing here?

Bothnia answered 23/3, 2018 at 14:6 Comment(0)
B
8

I've found the solution from a similar SO question: MyOtherModel comes from a different app so in my migration file I have to specify that app last migration as additional dependency, that is:

class Migration(migrations.Migration):
    dependencies = [
        ('contracts', '0014_my_previous_migration'),
        # THIS extra line solves the problem!
        ('my_other_app', '002_my_last_migration'),
    ]

    operations = [
        migrations.RunPython(set_item_things),
    ]
Bothnia answered 23/3, 2018 at 14:31 Comment(0)
W
4

You shouldn't touch models from other apps in your migration file, unless you specify proper dependencies to migrations of that other app. Basically if you want to use MyOtherModel from my_other_app, you have to add entry to dependencies in your migration to point to migration in my_other_app in which MyOtherModel exists and it is in desired state.

"Exists" and "desired state" needs some explanation here: when Django is dealing with migrations, it doesn't check actual model state that is currently in models.py of your application, but tries to reproduce your models from point in time, when migration was created. So if you want to use some_field from MyOtherModel, but that field was added in later migration, you have to point at least to that migration in which this field was introduced.

In same way, if later your field was removed, dependencies must point to one of migrations before that migration.

See Migrating data between third-party apps from Django documentation.

Warble answered 23/3, 2018 at 14:35 Comment(0)
F
1

Thanks to this link from this answer I solved my problem when trying to run a Django Test:

The error

LookupError: App 'old_app' doesn't have a 'OldModel' model.

The solution

def forwards(apps, schema_editor):
    try:
        OldModel = apps.get_model('old_app', 'OldModel')
    except LookupError:
        # The old app isn't installed.
        return
Farmergeneral answered 8/4, 2021 at 13:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.