为什么当我在迁移中使用已删除的模型时Django有时会崩溃?

aoyhnmkz  于 2022-12-20  发布在  Go
关注(0)|答案(1)|浏览(102)

我接手了一个混乱的项目,所以我一直在清理和删除大量的模型。有时候,我必须在删除之前将数据从模型复制到另一个模型中。我一直在编写可以同时完成这两项工作的迁移。有时候一切都很正常。但有时候它 * 不 * 工作。
流程:

  • 对代码库进行必要的更改,包括删除模型及其引用。
  • 创建具有以下特点的迁移:
  • 从模型中复制数据
  • 删除模型

以下是此类迁移的示例:

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

有时迁移会按预期运行。有时迁移会失败,并出现以下情况:
第一个月
我找不到有效的迁移和以这种方式失败的迁移之间有什么显著的区别,我已经有了很多这样的例子,并且绞尽脑汁试图找出区别。
我的理解是,通过RunPython()方法传递给函数的apps对象提供了所有模型的模拟 *,因为它们在迁移链中的那个点上存在 *,而不管代码库的状态如何,所以模型不再存在也没有关系。
然而,有时候确实如此。

u3r8eeie

u3r8eeie1#

刚刚有了一个小小的突破。下面是我当前上下文迁移的一个片段:

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():
        ...

在迁移过程中,我得到了FundManager的一个LookupError--尽管它实际上在两行以上就可以找到。
通过检查痕迹簿,我们发现了几条线索:

Traceback (most recent call last):
  File "/code/manage.py", line 20, in <module>
    main()
  File "/code/manage.py", line 16, in main
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.10/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.10/site-packages/django/core/management/__init__.py", line 413, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.10/site-packages/django/core/management/base.py", line 354, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.10/site-packages/django/core/management/base.py", line 398, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python3.10/site-packages/django/core/management/base.py", line 89, in wrapped
    res = handle_func(*args, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/django/core/management/commands/migrate.py", line 244, in handle
    post_migrate_state = executor.migrate(
  File "/usr/local/lib/python3.10/site-packages/django/db/migrations/executor.py", line 117, in migrate
    state = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial)
  File "/usr/local/lib/python3.10/site-packages/django/db/migrations/executor.py", line 147, in _migrate_all_forwards
    state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)
  File "/usr/local/lib/python3.10/site-packages/django/db/migrations/executor.py", line 227, in apply_migration
    state = migration.apply(state, schema_editor)
  File "/usr/local/lib/python3.10/site-packages/django/db/migrations/migration.py", line 126, in apply
    operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
  File "/usr/local/lib/python3.10/site-packages/django/db/migrations/operations/special.py", line 190, in database_forwards
    self.code(from_state.apps, schema_editor)
  File "/code/everest/migrations/0028_company.py", line 10, in migrate__fundmanager
    for fm in FundManager.objects.all():
  File "/usr/local/lib/python3.10/site-packages/django/db/models/query.py", line 280, in __iter__
    self._fetch_all()
  File "/usr/local/lib/python3.10/site-packages/django/db/models/query.py", line 1324, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/usr/local/lib/python3.10/site-packages/django/db/models/query.py", line 69, in __iter__
    obj = model_cls.from_db(db, init_list, row[model_fields_start:model_fields_end])
  File "/usr/local/lib/python3.10/site-packages/django/db/models/base.py", line 515, in from_db
    new = cls(*values)
  File "/usr/local/lib/python3.10/site-packages/dirtyfields/dirtyfields.py", line 37, in __init__
    reset_state(sender=self.__class__, instance=self)
  File "/usr/local/lib/python3.10/site-packages/dirtyfields/dirtyfields.py", line 163, in reset_state
    new_state = instance._as_dict(check_relationship=True)
  File "/usr/local/lib/python3.10/site-packages/dirtyfields/dirtyfields.py", line 99, in _as_dict
    all_field[field.name] = deepcopy(field_value)
  File "/usr/local/lib/python3.10/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/usr/local/lib/python3.10/copy.py", line 271, in _reconstruct
    state = deepcopy(state, memo)
  File "/usr/local/lib/python3.10/copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "/usr/local/lib/python3.10/copy.py", line 231, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/usr/local/lib/python3.10/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/usr/local/lib/python3.10/copy.py", line 265, in _reconstruct
    y = func(*args)
  File "/usr/local/lib/python3.10/site-packages/django/db/models/base.py", line 2154, in model_unpickle
    model = apps.get_model(*model_id)
  File "/usr/local/lib/python3.10/site-packages/django/apps/registry.py", line 211, in get_model
    return app_config.get_model(model_name, require_ready=require_ready)
  File "/usr/local/lib/python3.10/site-packages/django/apps/config.py", line 270, in get_model
    raise LookupError(
LookupError: App 'everest' doesn't have a 'FundManager' model.
  • dirtyfields包正在被调用,这提醒我们“模拟模型”只是表面的-下面的真实的基类仍然被引用
  • 通过dirtyfields的执行线程将导致另一个对apps.get_model()的调用,用于“FundManager”,但这一次它是真实的的django.apps,而不是迁移本身中使用的模拟django.apps

所以我认为这就是原因。我不知道正确的解决办法是什么。我想到的是:
1.避免使用像dirtyfields包这样很酷的基类(不是一个好的选择,因为它非常有用)
1.使用RunSQL代替RunPython进行数据迁移(与Python解决方案相比通常非常困难)

**更新:**刚刚想到第三个选项-编辑我的“0001_initial.py”迁移并从FundManager的CreateModel语句中删除dirtyfields基类:

bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model),

我 * 仍然 * 完全不知道为什么这对 * 这个 * 模型是必要的,而不是我在 * 相同 * 迁移中删除的另一个几乎相同的模型,它在没有LookupError的情况下是可删除的,* 尽管 * 仍然附加了它的dirtyfields基类...

相关问题