仅过滤 Django 中的注释

Posted

技术标签:

【中文标题】仅过滤 Django 中的注释【英文标题】:Filtering only on Annotations in Django 【发布时间】:2010-01-19 19:52:39 【问题描述】:

举个例子: http://docs.djangoproject.com/en/dev/topics/db/aggregation/#filter-and-exclude

Publisher.objects.filter(book__rating__gt=3.0).annotate(num_books=Count('book'))

有没有办法让过滤器只应用于注释,所以它会返回所有出版商,其中一些出版商的数量为 num_books=0?

【问题讨论】:

那么,您想要一份所有出版商的列表,每个出版商都标有他们的高评价书籍数量吗? 是的,该列表应包括没有书籍或只有低评级书籍的出版商。 【参考方案1】:

您可以在过滤器中使用注释变量。

publishers=Publisher.objects.annotate(num_books=Count('book')).filter(num_books__gte=2)

【讨论】:

我的意思是,我希望所有出版商都被退回,并且每个出版商都应该标注高评分的书籍数量。因此,出版商列表中应该包括少于两本高评分书籍的出版商,只有低评分书籍的出版商,以及根本没有书籍的出版商。如果我能更好地澄清这一点,请告诉我。【参考方案2】:

嗯,我认为你必须使用extra clause:

Publisher.objects.extra(select=
    'num_books': 'SELECT COUNT(*) ' + \
                 'FROM <your_app>_book ' + \
                 'WHERE <your_app>_book.publisher_id = ' + \
                       '<your_app>_publisher.id AND ' + \
                       'rating > 3.0'
)

【讨论】:

这是迄今为止我看到的最好的答案。如果我不必使用原始 sql,那就太好了。 这是真的!不过,这不应该导致任何兼容性问题。但是请告诉我,如果你发现了一种不用原始 sql 的方法! 您可能可以使用 QuerySet._as_sql(connection) 方法,由 Django 自己生成原始查询。小技巧,但仍然比自己编写更好。 @NathanTregillus 它不会创建任何额外的查询,因为它都是在 SQL 中完成的。只是因为查询更复杂,所以速度会慢一些,但没有什么比对每个对象都进行一次查询更慢。 这曾经是一个很好的答案,但在 2017 年已经过时了。django docs for extra 说这种方法已被弃用,只能作为最后的手段使用。他们甚至要求您为您的用例开一张票,以便他们可以通过annotate 支持它。【参考方案3】:
from django.db import models

Publisher.objects.annotate(
    num_books=models.Sum(
        models.Case(
            models.When(
                book__rating__gt=3.0,
                then=1,
            ),
            default=0,
            output_field=models.IntegerField(),
        )
    )
).filter(
    num_books=0,
)

【讨论】:

看起来很接近,除了最后不要按“num_books=0”过滤。应退回所有发布者。【参考方案4】:

启动 Django 2.0 可以使用聚合函数的filter参数:

from django.db.models import Q    
Publisher.objects.annotate(num_books=Count('book', filter=Q(book__rating__gt=3.0)))

答案基于cheat sheet。

【讨论】:

【参考方案5】:

你可以试试这样的:

Book.objects.values('publisher').annotate(num_books=Count('id'))

【讨论】:

这将是所有书籍的计数,而不仅仅是评分大于 3 的书籍。【参考方案6】:

我刚遇到这种问题。如果我对问题的解释和预期的解决方案是正确的,这就是我的工作解决方案: Publisher.objects.annotate(num_books=Count('book')).filter(book__rating__gt=3.0) 只需交换过滤器和注释位置。这是在 Django 1.9 版中完成的

【讨论】:

这与我的问题中的代码完全相同(甚至没有交换)。我遇到的问题是它没有显示拥有 0 本书的出版商。我根本不想过滤发布者,我只想过滤计数。 对不起,我忘了编辑你的示例代码。但这就是我想在我的查询集中使用过滤器和注释时实现的。不幸的是,我还没有清楚地阅读您关于 0 图书出版商的问题。

以上是关于仅过滤 Django 中的注释的主要内容,如果未能解决你的问题,请参考以下文章

Django 仅基于日期过滤日期时间

如何在 Django 中过滤对象以进行计数注释?

Django:结合来自两个过滤器查询的两个计数注释

在 Django 中注释相关和多重过滤的对象

Django ORM 使用过滤器进行注释

Django ORM:如何根据注释 timedelta 结果进行过滤