Django:如何从多对多迁移到外键?

J. *_*ers 8 django postgresql database-migration django-models django-migrations

我正在使用 Django 和 Django Rest Framework 构建 REST API 和服务器。我们正在使用 postgres 数据库。

我需要简化一个设计糟糕的关系。我们有一个模型 ( House)ManyToMany与另一个 ( City)有关系。实际上,当这是一种ForeignKey关系时就足够了。

我用谷歌搜索,找不到任何关于如何正确迁移的博客文章或文档。我只能找到另一种方式(FK 到 M2M)。

我 98% 确定服务器上的所有数据都与 FK 关系一致(这意味着我很确定所有房屋都只有一个城市)。由于多种原因,我们需要改变这种关系并且无法保留 M2M。

我害怕只是改变模型并运行makemigrationsmigrate. 我想知道,您如何正确地从 M2M 迁移到 FK?有什么需要我考虑的注意事项吗?如果令人惊讶地有多个城市的房屋,我该如何处理数据?如果重要的话,数据集仍然很小(少于 10k 个条目)。

非常感谢。

Tim*_*ony 9

编辑首先创建一个数据库备份

首先创建一个新的临时 FK 关系

_city = models.ForeignKey(...)
Run Code Online (Sandbox Code Playgroud)

并进行迁移

python manage.py makemigration
python manage.py migrate
Run Code Online (Sandbox Code Playgroud)

然后您需要通过创建一个空的迁移文件在相关应用程序中创建一个新的数据迁移

python manage.py makemigration --empty myhouseapp
Run Code Online (Sandbox Code Playgroud)

并在该文件中手动分配从 M2M 到 FK 的新关系。它看起来像:

from django.db import migrations


def save_city_fk(apps, schema):
    City = apps.get_model('myapp', 'City')
    House = apps.get_model('myapp', 'House')
    for house in House.objects.all():
        house._city = house.cities.all().first()  # Or whatever criterea you want
        house._city.save()


def save_city_m2m(apps, schema):
    # This should do the reverse
    City = apps.get_model('myapp', 'City')
    House = apps.get_model('myapp', 'House')
    for house in House.objects.all():
        if house._city:
            house.cities.add(house._city)


class Migration(migrations.Migration):

    dependencies = [
    ]

    operations = [
        migrations.RunPython(save_city_fk, save_city_m2m)
    ]
Run Code Online (Sandbox Code Playgroud)

删除 M2M 字段,并创建另一个迁移。

python manage.py makemigrations
Run Code Online (Sandbox Code Playgroud)

将 FK 重命名为_citytocity并创建最终迁移

python manage.py makemigrations
Run Code Online (Sandbox Code Playgroud)

然后迁移:

python manage.py migrate
Run Code Online (Sandbox Code Playgroud)