【问题标题】:When splitting a Django model, How to keep ForeignKey and ManyToMany relationships during data migration?拆分Django模型时,数据迁移时如何保持ForeignKey和ManyToMany关系?
【发布时间】:2016-11-06 15:05:50
【问题描述】:

我有一个做得太多的 Django 模型。这是该模型的缩写示例。基本上,它可以表示四种不同的Entity 类型,并且存在指向其他实体的递归ForeignKey 和ManyToMany 关系。

这个项目目前使用的是 Django 1.8.x 和 Python 2.7.x,但如果解决方案需要,我可以升级它们。

class Entity(models.Model):
    """
    Films, People, Companies, Terms & Techniques
    """

    class Meta:
        ordering = ['name']
        verbose_name_plural = 'entities'

    # Types:
    FILM = 'FILM'
    PERSON = 'PERS'
    COMPANY = 'COMP'
    TERM = 'TERM'
    TYPE_CHOICES = (
        (FILM, 'Film'),
        (PERSON, 'Person'),
        (COMPANY, 'Company'),
        (TERM, 'Term/Technique'),
    )

    created = models.DateTimeField(auto_now_add=True, auto_now=False)
    updated = models.DateTimeField(auto_now_add=False, auto_now=True)
    type = models.CharField(max_length=4, choices=TYPE_CHOICES, default=FILM)
    slug = models.SlugField(blank=True, unique=True, help_text="Automatically generated")
    name = models.CharField(max_length=256, blank=True)
    redirect = models.ForeignKey('Entity', related_name='entity_redirect', blank=True, null=True, help_text="If this is an alias (see), set Redirect to the primary entry.")
    cross_references = models.ManyToManyField('Entity', related_name='entity_cross_reference', blank=True, help_text="This is a 'see also' — 'see' should be performed with a redirect.")
    [... and more fields, some of them type-specific]

我意识到这相当混乱,我想删除“类型”并创建一个 EntityBase 类来抽象出所有公共字段,并创建新的 FilmPersonCompany ,以及继承自 EntityBase 抽象基类的 Term 模型。

创建新模型后,我想我了解如何编写数据迁移以将所有字段数据移至新模型(从Entity 迭代对象,通过type 过滤,创建新对象在适当的新模型中)... 除了 ForeignKey 和 ManyToMany 关系。也许我想错了,但是在迁移过程中,如果关系指向的新对象可能还不存在,我该如何转移这些关系呢?

我怀疑这可能意味着多步骤迁移,但我还没有找到正确的方法。

【问题讨论】:

  • 再想一想之后,我意识到我有the same problem as this fellow。我想知道是否按照我上面的建议来增加模型的概念清晰度,只会在关系和视图方面给我带来更多麻烦。要么我现在必须查询 4+ 模型才能得出所有“相关”结果,要么我必须摆弄像 django-gm2m 这样的东西。
  • 切换到具体继承(而不是抽象继承)似乎可以解决 那些 问题,但是 Django 社区的知名成员有 dire warnings about using concrete inheritance,所以……也许我应该保持原样,并清理我的管理代码以隐藏每种类型的不必要字段吗?

标签: django django-models django-migrations


【解决方案1】:

m2m 和 fk 字段没有什么神奇之处。这是我要遵循的程序...可能有点生硬,但会完成工作:

  1. 制作数据库的BACKKKUPPPPPPppp!!
  2. 再次备份!
  3. 创建新模型并迁移
  4. 编写一个新的数据迁移,它将手动迭代现有模型并逐个更新新模型。不要害怕这里的 for 循环,除非您在 db 中有数百万个条目。
  5. 删除冗余模型和/或字段,为此进行迁移。
  6. 运行这些迁移 :)

实际上,这意味着从“BACKKKUPPPPPPppp”进行大量恢复,直到迁移恰到好处。

需要注意的一件小事:

如果模型尚未保存,则 M2m 字段无法获取任何值(因为模型在第一次保存时获取其 ID)。我会在手动迁移中做类似的事情:

new_instance = NewModel()
new_instance.somefield = "whatever"
new_instance.meaning = 42
....
new_instance.save()
new_instance.that_m2m_field.add(some_related_obj)

当然,一定要详细阅读the docs,尤其是关于导入模型类的部分——你不能只导入它from myapp.models import MyModel,而是这样做:

MyModel = apps.get_model("myapp", "MyModel")

一个可能的绊脚石可能是您计划引入的模型继承。通常,您需要对子模型进行操作,并根据需要从那里访问父模型。可以通过隐式 ptr 属性访问父级 - 在您的示例中,它将是 entitybase_ptr 或类似的东西(这只是一个 OneToOne 字段)。然而,朝另一个方向前进(从父级到未知子级)并不那么简单,因为父级并不先验地知道其子级的类别。

【讨论】:

  • 好的,我明白了。所以基本上我必须创建所有新模型并迁移对象,然后,一旦迁移了所有对象,就使用原始模型作为参考进行迭代,这样我就可以查找哪些对象是相关的,然后找到对象的新实例(也许是通过 slug,因为那是独一无二的?)并重新添加 m2m 关系。
  • @bobtiki 嗯,这是最普遍的情况,是的。在实践中,这通常是多余的,我们可以在运行数据迁移时只使用一两个重复的字段,然后删除额外的字段来获得相同的结果。
猜你喜欢
  • 2019-07-09
  • 1970-01-01
  • 2012-02-27
  • 2011-11-10
  • 2016-04-29
  • 1970-01-01
  • 1970-01-01
  • 2016-02-10
  • 2022-07-22
相关资源
最近更新 更多