如何从django中的多对多中间模型中进行选择?

时间:2011-12-29 16:25:02

标签: python django django-models

我有书籍和人物模型:

from django.db import models

class Book(models.Model):
    author = models.ManyToManyField('Person')

class Person(models.Model):
    name = models.CharField(max_length=16)

我在这里简化了一下。如何制作一个django查询来获取书籍的所有作者?使用SQL我会在中间表上进行选择并将其与人员表连接以获取名称,但我不确定如何在此处执行类似操作...当然,Person表中有人是不是书的作者,或者我可以得到Person.objects.all()。

4 个答案:

答案 0 :(得分:3)

Filtering on annotations

一样简单1,2,3
from django.db.models import Count

Person.objects.annotate(count_book=Count('book')).filter(count_book__gt=0)

为了好奇,我从这个主题提出的每种方式生成SQL:

In [9]: Person.objects.annotate(count_book=Count('book')).filter(count_book__gt=0)
DEBUG (0.000) SELECT "testapp_person"."id", "testapp_person"."name", COUNT("testapp_book_author"."book_id") AS "count_book" FROM "testapp_person" LEFT OUTER JOIN "testapp_book_author" ON ("testapp_person"."id" = "testapp_book_author"."person_id") GROUP BY "testapp_person"."id", "testapp_person"."name", "testapp_person"."id", "testapp_person"."name" HAVING COUNT("testapp_book_author"."book_id") > 0  LIMIT 21; args=(0,)
Out[9]: [<Person: Person object>]

In [10]: Person.objects.exclude(book=None)
DEBUG (0.000) SELECT "testapp_person"."id", "testapp_person"."name" FROM "testapp_person" WHERE NOT (("testapp_person"."id" IN (SELECT U0."id" FROM "testapp_person" U0 LEFT OUTER JOIN "testapp_book_author" U1 ON (U0."id" = U1."person_id") LEFT OUTER JOIN "testapp_book" U2 ON (U1."book_id" = U2."id") WHERE (U2."id" IS NULL AND U0."id" IS NOT NULL)) AND "testapp_person"."id" IS NOT NULL)) LIMIT 21; args=()
Out[10]: [<Person: Person object>]

In [11]: Person.objects.filter(pk__in=Book.objects.values_list('author').distinct())
DEBUG (0.000) SELECT "testapp_person"."id", "testapp_person"."name" FROM "testapp_person" WHERE "testapp_person"."id" IN (SELECT DISTINCT U1."person_id" FROM "testapp_book" U0 LEFT OUTER JOIN "testapp_book_author" U1 ON (U0."id" = U1."book_id")) LIMIT 21; args=()
Out[11]: [<Person: Person object>]

也许这可以帮助您选择。

Personnaly,我更喜欢Chris的版本,因为它是最短的。另一方面,我不确定具有子查询的影响,这是其他两种方式的情况。也就是说,他们确实展示了令人感兴趣的QuerySet概念:

  1. Annonation ,是查询集的每个值的聚合。如果您使用聚合(Count('book')),那么您将获得书籍总数。如果您使用注释(Count('book')),那么您将获得查询集的每个值的总数(每个人)。此外,每个人都有'count_book'属性,这非常酷:Person.objects.annotate(count_book=Count('book')).filter(count_book__gt=0)[0].count_book

  2. 子查询,对于创建复杂查询或优化查询非常有用(例如,合并查询集,通用关系预取)。

答案 1 :(得分:3)

最简单的方法:

Person.objects.filter(book__isnull=False)

这将选择至少有一本书与之相关的所有人。

答案 2 :(得分:1)

您可以非常轻松地使用作者的所有ID Book.objects.all().values_list('author', flat=True).distinct()

正如jpic在下面指出的那样

Person.objects.filter(pk__in=Book.objects.values_list('author').distinct())将为您提供所有人物对象,而不仅仅是他们的身份。

答案 3 :(得分:-1)

django本书的这一章用非常类似于你的例子来回答你所有的模型需求,包括ManyToMany关系。 http://www.djangobook.com/en/2.0/chapter10/