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

Python 如何在django中从多对多中间模型中进行选择?,python,django,django-models,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,我会在中间表上进行选择,并将其与people表

我有书和人的模型:

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,我会在中间表上进行选择,并将其与people表连接以获得名称,但我不确定如何在此处执行类似操作。。。当然,Person表中有些人不是图书作者,或者我可以通过以下方法轻松获取Person.objects.all()

出于好奇,我根据本主题中提出的每种方法生成了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>]
[9]中的
:Person.objects.annotate(count\u book=count('book')).filter(count\u book\u gt=0)
调试(0.000)选择“testapp\u person”。“id”,“testapp\u person”。“name”,计数(“testapp\u book\u author”。“book\u id”)作为“COUNT\u book”,从“testapp\u person”左侧外部加入“testapp\u book\u author”上的“testapp\u person”。“id”=“testapp\u book\u author”。“person”;“id”,“testapp\u person”。“person”;“姓名”,“testapp\u person”。“姓名”计数(“测试书作者”,“书id”)>0限制21;args=(0,)
出[9]:[]
在[10]中:Person.objects.exclude(book=None)
DEBUG(0.000)从“testapp_person”中选择“testapp_person”。“id”,“testapp_person”。“name”从“testapp_person”中选择(从“testapp_person”中选择U0。“id”从“testapp_person”中选择U0。“id”从“testapp_person”中选择“id”。“name”从“testapp_person”中选择(U0.“id”=U1.“person”从(U1.“book_id”=U2.“id”)从(U2.)和“遗嘱人”。“id”不为空)限制21;args=()
出[10]:[]
[11]中:Person.objects.filter(pk\u In=Book.objects.values\u list('author').distinct())
调试(0.000)从“testapp_人”中选择“testapp_人”。“id”,“testapp_人”。“名称”,其中“testapp_人”。“id”位于(从“testapp_书”中选择不同的U1。“person_id”位于“testapp_书”U0左侧外部连接“testapp_书作者”U1 ON(U0.“id”=U1.“book_id”))限制21;args=()
Out[11]:[]
也许这能帮你选择

我个人更喜欢克里斯的版本,因为它是最短的。另一方面,我不确定子查询的影响,其他两种方法也是如此。也就是说,它们确实展示了相互测试的QuerySet概念:

  • 注释是查询集每个值的聚合。如果您使用aggregate(Count('book')),那么您将获得图书总数。如果您使用annotate(Count('book')),那么您将获得每个queryset值的图书总数(每个人)。另外,每个人都有一个非常酷的“count\u book”属性:
    person.objects.annotate(count\u book=count('book')).filter(count\u book\u gt=0)[0]。count\u book

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


  • django书的这一章用与您非常相似的示例回答了您所有的模型需求,包括许多关系

    您可以很容易地使用
    Book.objects.all().values\u list('author',flat=True).distinct()

    正如jpic在下面指出的那样

    Person.objects.filter(pk\u in=Book.objects.values\u list('author').distinct())
    将为您提供所有Person对象,而不仅仅是其id。

    最简单的方法:

    Person.objects.filter(book__isnull=False)
    

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

    看起来像是此stackoverflow问题的重复。链接和文档侧重于获取
    实例的所有
    ;不仅仅是所有有书的
    ,你能解释一下注释版本吗?当然,如果你有更具体的问题,请告诉我。我必须承认,一开始使用起来比理解起来容易,但是如果你把整个手册读几遍,你就会完全明白了!是的,我已经看过一次手册的大部分内容,并且再次点击了一些章节。很明显,我会多次返回!s/all/filter/在代码段中。这同样适用于:Person.objects.exclude(book=None)伟大的答案!是的。注意到在我看到你的评论之前。全脑放屁。不重复获得作者名单。我想这是在问题中暗示的,尽管我可能只是在想象。
    Person.objects.filter(book__isnull=False)