如何在 Django ORM 中执行 GROUP BY ... COUNT 或 SUM?
Posted
技术标签:
【中文标题】如何在 Django ORM 中执行 GROUP BY ... COUNT 或 SUM?【英文标题】:How to execute a GROUP BY ... COUNT or SUM in Django ORM? 【发布时间】:2018-01-14 19:19:05 【问题描述】:序幕:
这是SO中经常出现的一个问题:
Django Models Group By Django equivalent for count and group by How to query as GROUP BY in django? How to use the ORM for the equivalent of a SQL count, group and join query?我已经编写了一个关于 SO 文档的示例,但由于文档将于 2017 年 8 月 8 日关闭,我将按照this widely upvoted and discussed meta answer 的建议将我的示例转换为自我回答的帖子。
当然,我也很乐意看到任何不同的方法!
问题:
假设模型:
class Books(models.Model):
title = models.CharField()
author = models.CharField()
price = models.FloatField()
如何使用 Django ORM 在该模型上执行以下查询:
GROUP BY ... COUNT
:
SELECT author, COUNT(author) AS count
FROM myapp_books GROUP BY author
GROUP BY ... SUM
:
SELECT author, SUM (price) AS total_price
FROM myapp_books GROUP BY author
【问题讨论】:
【参考方案1】:我们可以在 Django ORM 上执行GROUP BY ... COUNT
或GROUP BY ... SUM
SQL 等效查询,分别使用annotate()
、values()
、django.db.models
的Count
和Sum
方法和可选order_by()
方法:
按...计数分组:
from django.db.models import Count
result = Books.objects.values('author')
.order_by('author')
.annotate(count=Count('author'))
现在结果包含一个带有两个键的字典:author
和count
:
author | count
------------|-------
OneAuthor | 5
OtherAuthor | 2
... | ...
GROUP BY ... SUM:
from django.db.models import Sum
result = Books.objects.values('author')
.order_by('author')
.annotate(total_price=Sum('price'))
现在结果包含一个 字典,其中包含两列:author
和 total_price
:
author | total_price
------------|-------------
OneAuthor | 100.35
OtherAuthor | 50.00
... | ...
2021 年 13 月 4 日更新
正如@dgw 在 cmets 中指出的那样,在模型使用元选项对行进行排序的情况下(例如 ordering
),order_by()
子句 对于成功至关重要聚合!
【讨论】:
您还应该添加带有分组依据和“拥有”过滤器的连接表。对我来说这是违反直觉的,因为在 SQL 中,您通常从父级开始,而在 django 中,您从子级开始。 @HenriettaMartingale 如果我理解正确你的意思,你可以在提取values
之前使用filter
。
你的意思是在annotate之后再次过滤,并且orm足够聪明,知道它需要做有吗?
这对我有用: statement_line.objects.filter(pay_date__lt='2019-10-31').select_related('ae').values('ae__opp_own').annotate(tots=Sum ('amt')).filter(tots__gt=0) 关键键是选择相关和父字段名称的双下划线。第二个过滤器确实转向“拥有”。 str([obj].query) 证实了这一点。另一个方便的东西。
也许应该强调order_by(...)
部分。如果模型使用不同的列进行排序,省略order_by()
子句将导致聚合失败。【参考方案2】:
通过 SUM() 分组,您可以获得几乎两个 dict 对象,如
inv_data_tot_paid =Invoice.objects.aggregate(total=Sum('amount', filter=Q(status = True,month = m,created_at__year=y)),paid=Sum('amount', filter=Q(status = True,month = m,created_at__year=y,paid=1)))
print(inv_data_tot_paid)
##output -'total': 103456, 'paid': None
不要尝试超过两个查询过滤器,否则你会得到类似的错误
【讨论】:
以上是关于如何在 Django ORM 中执行 GROUP BY ... COUNT 或 SUM?的主要内容,如果未能解决你的问题,请参考以下文章
使用SQL语言了解Django ORM中的分组(group by)和聚合(aggregation)查询
如何使用 django orm 进行嵌套 Group By?