Django:将ManyToManyField转换为ForeignKey

gue*_*eux 4 python django django-models

我们假设我创建了两个模型:

class Car(models.Model):
    name =  models.CharField(max_length=50)
    size =  models.IntegerField()


class Manufacturer(models.Model):
    name =  models.CharField(max_length=50)
    country =  models.CharField(max_length=50)
    car = models.ManyToManyField(Car)
Run Code Online (Sandbox Code Playgroud)

我在两个模型中添加了条目,然后我意识到每辆车只与一个独特的制造商有关.所以,我应该将ManyToManyField转换为ForeignKey:

class Car(models.Model):
    name =  models.CharField(max_length=50)
    size =  models.IntegerField()
    manufacturer = models.ForeignKey(Manufacturer)

class Manufacturer(models.Model):
    name =  models.CharField(max_length=50)
    country =  models.CharField(max_length=50)
Run Code Online (Sandbox Code Playgroud)

如何在不丢失参赛作品的情况下做到这一点?我试着查看南方文档,但我没有找到这种转换方式......

Tho*_*zco 7

这很不重要,我想你需要三次迁移:

  1. 添加ForeignKey.
  2. 转换ManyToManyForeignKey(使用forwards方法).
  3. 删除ManyToMany.

你可以将1和2或2和3合并在一起,但我不推荐它.
此外,您还应该实现backwards2 的方法.

2.的一个例子forwards是:

class Migration(SchemaMigration):
    def forwards(self, orm):
        for manufacturer in orm.Manufacturer.objects.all():
             for car in manufacturer.car.all():
                  car.manufacturer = manufacturer
                  car.save()
Run Code Online (Sandbox Code Playgroud)

请注意:

  • 这里还没有backwards方法.
  • 这需要进行广泛测试:迁移是您应该格外小心的事情.
  • 如果汽车有两个制造商,最后一个将保留.
  • 这是非常低效的,我们按每个制造商的每辆车进行查询!

您还需要在步骤2./3中更新使用这些关系的代码.


vab*_*ada 6

根据Thomas Orozco提供的优秀答案,我想为Django提供解决方案> = 1.7(基本上,第2点将ManyToMany转换为ForeignKey,这是Django较新版本的不同之处).所以这里是第二次迁移的代码:

class Migration(migrations.Migration):

    def migrate_m2m_to_fk(apps, schema_editor):
        Manufacturer = apps.get_model("app", "Manufacturer")
        for manufacturer in Manufacturer.objects.all():
             for car in manufacturer.car.all():
                  car.manufacturer = manufacturer
                  car.save()

    def migrate_fk_to_m2m(apps, schema_editor):
        Car = apps.get_model("app", "Car")
        for c in Car.objects.all():
            if c.manufacturer:
                c.manufacturer.car.add(c)
                c.manufacturer.save()

    operations = [
        migrations.RunPython(migrate_m2m_to_fk, migrate_fk_to_m2m)
    ]
Run Code Online (Sandbox Code Playgroud)

"app"是模型所在的Django应用程序.显示了正向和反向迁移代码(正如托马斯所提到的,如果预先存在多个关系,运行此迁移可能会导致数据丢失,因此请注意).