Тесты Django не выполняются при использовании определенной модели в файле миграции

Я вручную создал файл миграции данных для конкретного приложения Django 1.11:

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),
    ]

Когда я бегу python manage.py migrate все работает как положено.
Но всякий раз, когда я запускаю свой тест с использованием pytest, я получаю это:

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'

Таким образом, похоже, что конфигурация приложения не настроена должным образом, и это уже странно, потому что команда migrate прошла гладко.

Во всяком случае: это содержание my_other_app/apps.py:

from django.apps import AppConfig

class MyOtherAppConfig(AppConfig):
    name = 'my_other_app'

И в принципе очень похож на все остальные apps.py сидя в каталогах других приложений, за исключением, конечно, для имени.

Поэтому я думаю, что конфигурация должна быть правильной, но по каким-то причинам мои тесты не будут выполняться.

Единственное исправление - удалить любую ссылку на my_other_app из файла миграции.

Я уже пытался добавить это к my_other_apps/__init__.py:

default_app_config = 'my_other_apps.apps.MyOtherAppConfig'

но ничего не меняется.

Я уже пытался увидеть, есть ли внутри круговые зависимости my_other_apps/models.py но это не так.

Что мне здесь не хватает?

3 ответа

Решение

Я нашел решение из аналогичного вопроса SO: MyOtherModel исходит из другого приложения, поэтому в моем файле миграции я должен указать последнюю миграцию приложения как дополнительную зависимость, то есть:

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),
    ]

Не следует прикасаться к моделям из других приложений в вашем файле миграции, если вы не укажете правильные зависимости для миграции этого другого приложения. В основном, если вы хотите использовать MyOtherModel от my_other_app, вы должны добавить запись в dependencies в вашей миграции, чтобы указать на миграцию в my_other_app в котором MyOtherModel существует и находится в желаемом состоянии.

"Существует" и "желаемое состояние" нуждается в некотором объяснении здесь: когда Django имеет дело с миграциями, он не проверяет фактическое состояние модели, которое в данный момент находится в models.py вашего приложения, но пытается воспроизвести ваши модели с момента времени, когда была создана миграция. Так что если вы хотите использовать some_field от MyOtherModel, но это поле было добавлено в более поздней миграции, вы должны указать хотя бы ту миграцию, в которой это поле было введено.

Таким же образом, если позднее ваше поле было удалено, зависимости должны указывать на одну из миграций до этой миграции.

См. Миграция данных между сторонними приложениями из документации Django.

Благодаря этой ссылке из этого ответа я решил свою проблему при попытке запустить тест Django:

Ошибка

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

Решение

      def forwards(apps, schema_editor):
    try:
        OldModel = apps.get_model('old_app', 'OldModel')
    except LookupError:
        # The old app isn't installed.
        return
Другие вопросы по тегам