Django模型拆分时,数据迁移时如何保持ForeignKey和ManyToMany关系?
When splitting a Django model, How to keep ForeignKey and ManyToMany relationships during data migration?
我的 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]
我意识到这很混乱,我想删除 'type' 并制作一个 EntityBase
class 来抽象出所有公共字段,并创建新的 [ =13=、Person
、Company
和 Term
模型继承自 EntityBase
抽象基础 class.
创建新模型后,我想我了解了如何编写数据迁移以将所有字段数据移动到新模型(迭代来自 Entity
的对象,通过 [=19 过滤) =],在适当的新模型中创建新对象)... 除了 ForeignKey 和 ManyToMany 关系。也许我的想法是错误的,但是在迁移过程中,当关系指向的新对象可能还不存在时,我该如何转移这些关系?
我怀疑这可能意味着多步迁移,但我还没有找到正确的方法。
m2m 和 fk 字段没有什么神奇之处。这是我要遵循的程序...可能有点生硬,但可以完成工作:
- 制作数据库的BACKKKUPPPPPPppp!!
- 再次备份!
- 创建新模型并迁移
- 编写一个新的数据迁移,将手动迭代现有模型并更新新模型,one-by-one。不要害怕这里的
for
循环,除非你在数据库中有数百万个条目。
- 删除冗余模型and/or字段,为此进行迁移。
- 运行 那些迁移:)
实际上,这意味着从 "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,尤其是关于导入模型的部分 class - 您不能只导入它 from myapp.models import MyModel
,改为:
MyModel = apps.get_model("myapp", "MyModel")
一个可能的绊脚石可能是您计划引入的模型继承。通常,您需要在 child 模型上操作,并在需要时从那里访问 parent。 Parent 可以通过隐式 ptr
属性访问 - 在您的示例中它将是 entitybase_ptr
或类似的东西(这只是一个 OneToOne 字段)。然而,从另一个方向(从 parent 到未知的 child)并不那么简单,因为 parent 事先不知道它的 class 是什么child.
我的 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]
我意识到这很混乱,我想删除 'type' 并制作一个 EntityBase
class 来抽象出所有公共字段,并创建新的 [ =13=、Person
、Company
和 Term
模型继承自 EntityBase
抽象基础 class.
创建新模型后,我想我了解了如何编写数据迁移以将所有字段数据移动到新模型(迭代来自 Entity
的对象,通过 [=19 过滤) =],在适当的新模型中创建新对象)... 除了 ForeignKey 和 ManyToMany 关系。也许我的想法是错误的,但是在迁移过程中,当关系指向的新对象可能还不存在时,我该如何转移这些关系?
我怀疑这可能意味着多步迁移,但我还没有找到正确的方法。
m2m 和 fk 字段没有什么神奇之处。这是我要遵循的程序...可能有点生硬,但可以完成工作:
- 制作数据库的BACKKKUPPPPPPppp!!
- 再次备份!
- 创建新模型并迁移
- 编写一个新的数据迁移,将手动迭代现有模型并更新新模型,one-by-one。不要害怕这里的
for
循环,除非你在数据库中有数百万个条目。 - 删除冗余模型and/or字段,为此进行迁移。
- 运行 那些迁移:)
实际上,这意味着从 "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,尤其是关于导入模型的部分 class - 您不能只导入它 ,改为:from myapp.models import MyModel
MyModel = apps.get_model("myapp", "MyModel")
一个可能的绊脚石可能是您计划引入的模型继承。通常,您需要在 child 模型上操作,并在需要时从那里访问 parent。 Parent 可以通过隐式 ptr
属性访问 - 在您的示例中它将是 entitybase_ptr
或类似的东西(这只是一个 OneToOne 字段)。然而,从另一个方向(从 parent 到未知的 child)并不那么简单,因为 parent 事先不知道它的 class 是什么child.