【问题标题】:How to perform a datamigration when moving from one-to-one to one-to-many从一对一迁移到一对多时如何执行数据迁移
【发布时间】:2012-07-13 13:06:31
【问题描述】:

我正在使用south 来管理迁移,但我遇到了困难。基本上我有以下设置:

App1

class A(models.Model):
  # bunch of attributes

App2

class B(models.Models):
  instance_a = models.OneToOneField(A, null=True, blank=True, 
                                    editable=False)

现在,我想从这个转到这个:

App1

class A(models.Model):
  instance_b = models.ForeignKey(B, null=True, blank=True)

App2

class B(models.Models):
  # other attributes

我的主要问题是我不能丢失数据。所以基本上在迁移结束时,之前映射到对象 B 的所有对象 A 都应该保留该映射。例如,如果将 id 为 7 的对象 A 映射到 id 为 8 的对象 B,则在此过程结束时应保留此映射。

我尝试了几项来自架构迁移与临时占位符和数据迁移的混合。但是,我最终总是在同一个地方,即在执行数据迁移时,我不再具有以前的关系以访问正确的属性。例如,B.instance_a 不再可用。

我想听听你对两件事的看法:

  • 首先,仅使用南迁移是否可行。
  • 其次,我该如何进行。

谢谢

【问题讨论】:

    标签: python django django-models django-south


    【解决方案1】:

    终于过了一段时间,我得到了一个django-south 的程序,可能对其他人有帮助。关键在于南的depends_on 功能(http://south.aeracode.org/wiki/Dependencies)。我分 4 步完成:

    第一

    • 为模型A 中的外键值创建占位符。

    所以模型A变成:

    class A(models.Model):
        instance_b_placeholder = models.ForeignKey(A, null=True, blank=True)
    

    现在只需运行manage.py schemamigration app1 --auto

    第二

    • 创建数据迁移,以便我们可以复制值。目标是在数据库中复制数据,然后重命名属性并删除旧属性。问题manage.py datamigration app1 update_fields。我选择将数据迁移保留在app1。如果您不这样做,请确保它在上次迁移之后运行。

    这是数据迁移编码:

    # Forwards:
    
    for b in orm['app2.B'].objects.filter(instance_b__isnull=False):
            b.instance_a.instance_b_placeholder = b
            b.instance_a.save()
    
    # Backwards:
    
    for r in orm['app1.A'].objects.filter(instance_b_placeholder__isnull=False):
            r.instance_b_placeholder.instance_a = r
            r.instance_b_placeholder.save()
    

    第三

    • 从模型B 中删除字段instance_b,并确保在上一步中创建的迁移之后运行迁移。

    模型B 变为:

    class B(models.Model):
         # etc...
    

    发出manage.py schemamigration app2 --auto 并编辑迁移,将之前的迁移添加到depends_on

    depends_on = (
        ("app1", "<migration_number>_update_fields"),
    )
    

    第四步

    • 重命名占位符。这是通过更改代码中的名称并编辑迁移来实现的。编辑是必要的,因为south 倾向于删除和添加一个新列,但我们只希望它重命名该列。

    • 这个迁移应该在最后一个地方运行,所以我让它依赖于前一个。

    代码如下:

    depends_on = (
        ("app2", "<previous_migration_here>"),
    )
    
    # Forwards:
    
        db.rename_column('app1_a', 'instance_b_placeholder_id', 'instance_b_id')
    
    # Backwards:
    
        db.rename_column('app1_a', 'instance_b_id', 'instance_b_placeholder_id')
    

    就是这样。我不知道是否还有很多其他方法可以做到这一点,但至少这对我有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-09
      • 2017-04-14
      • 2011-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多