带有额外字段的 Django ManyToMany 字段不会显示在两个关系上

Posted

技术标签:

【中文标题】带有额外字段的 Django ManyToMany 字段不会显示在两个关系上【英文标题】:Django ManyToMany field through with extra fields does not show on both relations 【发布时间】:2016-06-09 07:51:47 【问题描述】:

我有一堂课Assembly

class Assembly(models.Model):

    room = models.ForeignKey("Room", related_name="assemblies")
    name = models.CharField(max_length=200)
    number = models.IntegerField()
    position = models.CharField(max_length=200, blank=True)
    components = models.ManyToManyField("material.Component", through="m2m_Assembly_Components")
    connections = models.ManyToManyField("Assembly", through="Connection")
    category = models.ForeignKey("Category", default=0)
    notes = models.TextField(blank=True)

在自身实例之间具有多对多关系 (connections)。 我使用了一个中间表Connection,这样我就可以为Assembly 的两个实例之间的连接提供额外的字段。

class Connection(models.Model):

    source = models.ForeignKey("Assembly", related_name="source_assembly", null=True)
    destination = models.ForeignKey("Assembly", related_name="destination_assembly", null=True)
    length = models.IntegerField(null=True, blank=True)

如果我有两个程序集,假设 A 和 B,我通过定义一个以 A 作为源和 B 作为目标的新连接来连接它们,我得到 B 作为 A 的连接 (A.connections.all()),但我没有获取 A 作为 B 的连接。

如果我不使用中间表,只使用models.ManyToManyField("Assembly"),我将 A 作为 B 的连接,将 B 作为 A 的连接。

我的问题是什么?

【问题讨论】:

嗯..没有指定related_name,你有没有从B方面尝试过assembly_set 如果我没有指定related_name,我会得到一个错误(不记得到底是哪个),因为我两次使用相同的ForeignKey。 【参考方案1】:

我认为您需要为您的 ManyToManyField 指定 through_fields 参数。

当 Django 自动生成您的直通模型时,它知道其中的两个ForeignKeys 中的哪一个对应关系的“本地”端,哪个是“远程”端。但是,当您指定自定义中间模型时,这会变得更加困难。有可能 Django 只是将中间模型中的第一个 ForeignKey 指向正确的目标模型,在这两种情况下恰好是 source (尽管我不确定这是否真的如此,如果它是,它可能是一个错误)。

尝试看看使用through_fields=('source', 'destination') 是否有帮助。

【讨论】:

我刚刚尝试了through_fields,但没有帮助。 @Bouni 我在我的回答中解释了为什么您不会在 A 和 B 之间获得对称连接。但是,您必须使用 through_fields 正确定义什么是源和什么是目标。 @AKS 在the other answer 中有一个优点——当您使用自定义的through 模型时,关系自动不再对称,这意味着关系的每一边都会看到一组不同的对象。对称性是通过在中间表中为每对相关对象使用两行来实现的,因此如果您想使用自定义直通模型,则必须自己执行此操作。这也意味着任何额外的数据都将在数据库中存储两次。 @koniiiik 在数据库中添加两行非常好,但它确实需要额外的管理和照顾。 :) 我想我会走简单的路,使用默认的 Django ManyToMany 字段,不用额外的字段。无论如何感谢您的建议!【参考方案2】:

跟进@koniiiik 对through_fields 的回答:

使用中间模型的递归关系总是被定义为非对称的——即symmetrical=False——因此,有“source”“target”的概念。在这种情况下,'field1' 将被视为关系的“source”'field2' 将被视为“target”

而且,在你的情况下,它是 sourcedestination

由于您使用的是中间模型Connection,因此关系不再对称。所以A.connections.all()B.connections.all()会返回不同的结果。

A.connections.all() #all assemblies where A is source
B.connections.all() # all assemblies where B is source

如果添加连接:

Connection(source=A, destination=B)

您可以使用以下命令找到所有以 B 为目标的程序集:

B.destination_assembly.all().values_list('source', flat=True) # this will include A

【讨论】:

...values_list('source_assembly', flat=True) 的建议根本没有给我任何结果。 您是否使用through_fields 更新了声明,例如models.ManyToManyField("Assembly", through="Connection", through_fields=('source', 'destination')) 是的,由于@koniiik 的建议,我在尝试您的建议前几分钟添加了 through_fields 如果可能的话;请删除现有条目,迁移,添加新连接,看看你是否得到了B.destination_assembly.all()的任何结果? 好的,我会尝试作为最后的测试:-)

以上是关于带有额外字段的 Django ManyToMany 字段不会显示在两个关系上的主要内容,如果未能解决你的问题,请参考以下文章

Django如何从带有额外字段的ManyToManyField中删除?

JPA 2:通过不在带有额外字段的多对多中工作来订购

Django 中的 OneToOne、ManyToMany 和 ForeignKey 字段有啥区别?

如何更新Django 的ManyToMany 字段

Django:模型设计,ManyToMany 属性字段?

Django:快速检索manyToMany字段的ID