拆分Django模型时,如何在数据迁移期间保留ForeignKey和ManyToMany关系?

时间:2016-07-04 21:06:49

标签: django django-models django-migrations

我有一个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类来抽象出所有常见字段,并创建新的FilmPersonCompanyTerm模型EntityBase抽象基类。

创建新模型后,我想我了解如何编写数据迁移以将所有字段数据移动到新模型(迭代Entity上的对象,通过type过滤,在适当的新模型中创建新对象)... 除了 ForeignKey和ManyToMany关系。也许我正在以错误的方式思考这个问题,但是如果在迁移过程中,关系指向的新对象可能还不存在,我怎样才能转移这些关系?

我怀疑这可能意味着多步迁移,但我还没有找到正确的方法来实现这一目标。

1 个答案:

答案 0 :(得分:1)

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

  1. 制作数据库的BACKKKUPPPPPPppp !!
  2. 再做一次备份!
  3. 创建新模型和迁移
  4. 编写一个新的数据迁移,它将手动迭代现有模型并逐个更新新模型。除非你在db中有数百万个条目,否则不要害怕for循环。
  5. 删除冗余模型和/或字段,为此进行迁移。
  6. 运行这些迁移:)
  7. 在实践中,这意味着从“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字段)。然而,朝着另一个方向前进(从父母到未知的孩子)并不那么简单,因为父母不知道其孩子的等级是什么。