【问题标题】:How to execute a GROUP BY ... COUNT or SUM in Django ORM?如何在 Django ORM 中执行 GROUP BY ... COUNT 或 SUM?
【发布时间】:2018-01-14 19:19:05
【问题描述】:

序幕:

这是SO中经常出现的一个问题:

我已经编写了一个关于 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
    

【问题讨论】:

    标签: python django group-by django-orm


    【解决方案1】:

    我们可以在 Django ORM 上执行GROUP BY ... COUNTGROUP BY ... SUM SQL 等效查询,分别使用annotate()values()django.db.modelsCountSum 方法和可选order_by() 方法:

    • 按...计数分组:

       from django.db.models import Count
      
       result = Books.objects.values('author')
                             .order_by('author')
                             .annotate(count=Count('author'))
      

      现在结果包含一个带有两个键的字典authorcount

         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'))
      

      现在结果包含一个 字典,其中包含两列:authortotal_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}
    

    不要尝试超过两个查询过滤器,否则你会得到类似的错误

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-06
      • 2015-08-21
      • 1970-01-01
      • 1970-01-01
      • 2012-06-15
      • 1970-01-01
      • 2021-08-01
      相关资源
      最近更新 更多