Django ORM 处理多个多对多关系的方式

Posted

技术标签:

【中文标题】Django ORM 处理多个多对多关系的方式【英文标题】:Django ORM way of going through multiple Many-to-Many relationship 【发布时间】:2011-06-27 08:10:51 【问题描述】:

亲爱的人们试图帮助他人,

我正在尝试弄清楚如何让 Django 为我进行连接,而无需编写自定义 SQL。

假设我有以下模型

class Parent(models.Model): 
  name =  models.CharField()
  children = models.ManyToManyField(Child, through="Parent_Child", related_name="parents")

class Parent_Child(models.Model):
  parent = models.ForeignKey(Parent, related_name='attached_children')
  child = models.ForeignKey(Child,  related_name='attached_parents')

class Child(models.Model): 
  name = models.CharField() 
  toys = models.ManyToManyField(Toy, hrough="Child_Toy", related_name="toy_owners")

class Child_Toy(models.Model): 
  child = models.ForeignKey(Child, related_name='attached_toys') 
  toy =  models.ForeignKey(Toy, related_name='toy_owner')

class Toy(models.Model): 
  name = models.CharField

父母可以有多个孩子。一个孩子可以有多个父母。一个孩子可以拥有多个玩具。玩具可以由多个孩子拥有。

我想获取父母子女拥有的所有玩具的清单。

所以,我可以执行以下操作: parent.children.all()child.toys.all()

我想做的是类似于parent.children.toys.all() 当我尝试这样做时,我得到:AttributeError: 'ManyRelatedManager' object has no attribute 'toys'。我确实理解错误 - parent.children 返回多条记录。这是意料之中的。我想不通的是如何给 Django 提示我希望它在其查询中添加一个额外的连接。

有没有办法可以在 Django 中执行此连接,或者我是否需要使用自定义 SQL 才能执行此操作?

请注意:以上只是为了说明我的问题,我使用的实际模型并不那么相关。我的问题实际上是想弄清楚如何通过 Django 中的多个 M2M 关系加入,而不必求助于 SQL。

提前感谢您的帮助。谢谢!

【问题讨论】:

我不想写一个新的答案,但会添加一个文档链接:docs.djangoproject.com/en/dev/topics/db/queries/… 了解有关 django JOIN 的信息,这将有助于了解链接 filter() 如何影响您的m2m查询 【参考方案1】:

简单地写如下:

Toy.objects.filter(toy_owners__parents=parent)

【讨论】:

_owners 和 __parents 是什么意思? @Harun-Ur-Rashid 您已经很久没有提出您的问题了,所以我希望您已经找到或弄清楚了,但对于可能有相同问题的其他人:_owners本身是不完整的; toy_owners 由 OP 指定为 related_name 用于 Child 类上的 toys 字段。至于__parents__ 告诉 Django 引用一个相关对象,在本例中为 parents,即 related_name 用于 children 类上的 children 字段。【参考方案2】:

如果你没有在中间表Parent_ChildChild_Toy 中存储额外的信息,你可以把它们排除在外——Django 会自动为你创建它们。因此,简化的设置如下所示:

class Parent(models.Model): 
    name =  models.CharField(max_length=80)
    children = models.ManyToManyField('Child', related_name="parent")

class Child(models.Model): 
    name = models.CharField(max_length=80) 
    toys = models.ManyToManyField('Toy', related_name="owner")

class Toy(models.Model): 
    name = models.CharField(max_length=80)

您可以使用field lookups 查询特定父母的玩具。

Toy.objects.filter(owner__parent__id=1)

或者:

Toy.objects.filter(owner__parent=parent)

resulting SQL 看起来像这样:

SELECT "toy"."id", "toy"."name" FROM "toy" 
    INNER JOIN "child_toys"
        ON ("toy"."id" = "child_toys"."toy_id") 
    INNER JOIN "child"
        ON ("child_toys"."child_id" = "child"."id") 
    INNER JOIN "parent_children" 
        ON ("child"."id" = "parent_children"."child_id")
    WHERE "parent_children"."parent_id" = 1

【讨论】:

以上是关于Django ORM 处理多个多对多关系的方式的主要内容,如果未能解决你的问题,请参考以下文章

15)django-ORM(多对多)

Django

Django学习-9-ORM多对多操作

Django框架之ORM的相关操作之多对多三种方式

Django ORM:优化涉及多对多关系的查询

Django中ORM多对多表的操作