Почему Django случайно ломается, когда я использую удаленную модель в миграции?
Я унаследовал беспорядочный проект, поэтому в процессе уборки я удалил много моделей. Иногда мне приходится копировать дату из модели в другую модель перед удалением. Я написал миграции, которые делают и то, и другое. Все работает хорошо - иногда. И, кажется, случайным образом, иногда это не работает.
Процесс:
- Внесите необходимые изменения в кодовую базу, включая удаление модели и ссылок на нее.
- Создайте миграцию, которая:
- копирует данные из модели
- удаляет модель
Вот пример того, как выглядела бы такая миграция:
from django.db import migrations, models
def doit(apps, schema_editor):
OldModel = apps.get_model("myapp", "OldModel")
NewModel = apps.get_model("myapp", "NewModel")
for obj in OldModel.objects.all():
...transform and save data in NewModel...
class Migration(migrations.Migration):
dependencies = [("myapp", "0001_initial")]
operations = [
migrations.RunPython(doit, reverse_code=migrations.RunPython.noop),
migrations.DeleteModel(name="OldModel"),
]
Иногда миграция выполняется, как и ожидалось. В других случаях миграция завершается неудачей:
LookupError: App 'myapp' doesn't have a 'OldModel' model.
Я не могу найти никакой заметной разницы между миграциями, которые работают, и миграциями, которые взрываются таким образом. У меня было множество примеров каждой из них, и я изрядно поломал голову, пытаясь найти разницу.
Насколько я понимаю, объект apps
, переданный вашей функции методом RunPython()
, обеспечивает симуляцию всех моделей как они существуют в данный момент в цепочке миграции , независимо от состояния вашей кодовой базы, поэтому не должно иметь значения, что модель больше не существует.
И все же, случайным образом, иногда это происходит?
Только что произошел небольшой прорыв. Вот фрагмент моей текущей миграции для контекста:
def migrate__fundmanager(apps, schema_editor):
Company = apps.get_model("everest", "Company")
Fund = apps.get_model("everest", "Fund")
FundManager = apps.get_model("everest", "FundManager")
for fm in FundManager.objects.all():
...
На этом этапе миграции я получил ошибку LookupError для FundManager
- хотя его можно было найти буквально двумя строками выше.
Изучение книги слежения дало несколько подсказок:
- вызывается пакет
dirtyfields
, что является напоминанием о том, что "симулированная модель" - это только кожа - реальные базовые классы под ней все еще ссылаются - поток выполнения, проходящий через
dirtyfields
, приводит к еще одному вызовуapps.get_model()
для "FundManager", но на этот раз это реальныйdjango.apps
, а не симулированный, используемый в самой миграции .
Вот что, как мне кажется, является причиной этого. Я понятия не имею, какое правильное решение. Все, что приходит на ум, это:
- избегать использования крутых базовых классов, таких как пакет
dirtyfields
(не лучший вариант, так как он очень полезен) - использовать
RunSQL
для миграции данных вместоRunPython
(часто очень сложно по сравнению с решением на Python)
UPDATE: Только что придумал третий вариант - редактирование моей миграции "0001_initial.py" и удаление dirtyfields
базового класса из CreateModel
заявления FundManager:
bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model),
Это сработало. Я по-прежнему совершенно не понимаю, почему это было необходимо для этой модели, но не для другой, почти идентичной, которую я удаляю в той же самой миграции, и которая была удалена без LookupError
несмотря на то, что к ней все еще был присоединен базовый класс...dirtyfields