在django中将多对多关系转换为一对多关系

在django中将多对多关系转换为一对多关系,django,python-3.x,Django,Python 3.x,我正在努力实现的目标 到目前为止,我们已经有了如下所示的多对多关系的模型 class A(models.Model): name = models.CharField(max_length=255) data = models.ManyToManyField(B, related_name='data', through='C') class B(models.Model): name=models.CharField(max_length=255) class C(

我正在努力实现的目标

到目前为止,我们已经有了如下所示的多对多关系的模型

class A(models.Model):
    name = models.CharField(max_length=255)
    data = models.ManyToManyField(B, related_name='data', through='C')

class B(models.Model):
    name=models.CharField(max_length=255)

class C(models.Model):
    a = ForeignKey(A)
    b = ForeignKey(B)
    c = model.CharField(max_length=255)
    active = models.BooleanField(default=True)
    and so on....
尽管Django会自动为多对多关系创建一个中间表,但如果我们不显式定义它,您可以注意到,我已经显式定义了第三个中间表来存储关于每个关系的额外元数据

因此,根据新的业务需求,我们需要从表中删除多对多关系,并将其替换为一对多关系,即B的每个记录可以有多个对obj A的引用,但A的记录将始终有一个对obj的引用

此外,由于这些表已经在生产中使用,因此我们也需要迁移现有数据

到目前为止我已经尝试过的事情

从模型A中删除多对多

class A(models.Model):
    name = models.CharField(max_length=255) 
    data = models.ForeignKey(B)
已更新模型C的现有行

例如:

#this might return more than one obj because of many to many relationship
test = C.objects.filter(a__id=<a obj id>)

for t in test:
   t.b_id = <some id pointing to record of B>
#由于多对多关系,这可能会返回多个obj
test=C.objects.filter(a\u id=)
对于测试中的t:
t、 b_id=
基本上,在完成上述步骤后,它将用a_id更新记录,以将b的某个obj的id更新

我面临的问题

当我试图运行
makemigrations
时,它询问数据的默认值,我不确定该放什么。在模型A中,我可以将数据字段指向B的任何记录,但是我将失去第三个表C的使用,而且表C在整个系统中被广泛使用。 那么,我需要放弃表C吗? 另外,我想知道有没有更好的方法来改变多对多对一对多的关系。
任何帮助都会很有用。

在这种情况下,当您的数据位于中间表中时, 我建议你做以下几点

  • A
    表中添加和临时列
  • 然后执行
    python manage.py makemigrations

  • 创建一个空迁移文件,并使其运行此代码以将所有
    A-B
    关系数据从表
    C
    复制到表
    A
  • 换成这个
  • python manage.py迁移
  • 这应该是好事。 之后,如果要删除表
    C
    ,则由您决定,因为其中的数据会安全地迁移到表
    A

    class A(models.Model):
        name = models.CharField(max_length=255)
        data = models.ManyToManyField(B, related_name='data', through='C')
        # the on_delete part is not related, I added it just to avoid errors.
        tmp_data = models.ForeignKey(B, on_delete=models.DO_NOTHING)
    
    # Command
    python manage.py makemigrations <<<APP_NAME>>> --empty
    
    # Inside Empty migrations file
    def migrate_c_to_a(apps, schema_editor):
        C = apps.get_model('<<<APP NAME>>>', 'C')
        for c in C.objects.all().iterator():
            a = c.a
            a.tmp_data = c.b
            a.save()
    
    class Migration(migrations.Migration):
        ...
        ...
        operations = [
            migrations.RunPython(migrate_c_to_a)
        ]
    
    ...
    operations = [
            migrations.RemoveField(
                model_name='a',
                name='tmp_data',
            ),
            migrations.RemoveField(
                model_name='a',
                name='data',
            ),
            migrations.AddField(
                model_name='a',
                name='data',
                field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='tsting.B'),
            ),
        ]
    ...
    
    ...
    operations = [
            migrations.RemoveField(
                model_name='a',
                name='data',
            ),
            migrations.RenameField(
                model_name='a',
                old_name='tmp_data',
                new_name='data'
            ),
        ]
    ...