【问题标题】:How can I filter by date to get this week vs previous week Counts from a QuerySet?如何按日期过滤以从 QuerySet 中获取本周与前一周的计数?
【发布时间】:2017-05-19 09:44:16
【问题描述】:

我有以下两个带注释的查询集,它们是通过从数据集中提取this_wkprev_wk 分别生成的,然后执行注释。我想在归还它们之前将它们合并在一起。

data = {
    'this_wk': QuerySet([{'this_total': 3, 'dept': 2}, 
                         {'this_total': 2, 'dept': 1}, 
                         {'this_total': 1, 'dept': 3}]),
    'prev_wk': QuerySet([{'prev_total': 2, 'dept': 3}])}

预期:

QuerySet([{'this_total': 3, 'prev_total': None, 'dept': 2},
          {'this_total': 2, 'prev_total': None, 'dept': 1}, 
          {'this_total': 1, 'prev_total': 2, 'dept': 3}]),

或者,有没有办法直接聚合它,以便我直接从一组带有日期字段的项目中获取?以下是我目前生成两个 QuerySet 的方式:

previous_items = Widget.objects.filter(start__gte=prev_start).filter(start__lte=prev_end)
prev_ranking = previous_items.values('dept').annotate(prev_total=Count('dept'))

this_items = Widget.objects.filter(start__gte=this_start
    ).filter(start__lte=this_end)
this_ranking = this_items.values('dept'
    ).annotate(this_total=Count('dept')
    ).order_by('-this_total')

有没有办法将这两个注释链接在一起,以便我最终得到一个单独的 QuerySet,根据本周使用的 Dept 对每个 Dept 进行分组和排名,但还添加了一个字段这表示前一周使用了多少Widgets

我在想这样的事情:

items = Widget.objects.filter(start__gte=prev_start
    ).filter(start__lte=this_end
    ).annotate(prev_total=Count('dept')  # can I restrict this to the previous week?
    ).annotate(this_total=Count('dept')  # can I restrict this to the current week?
    ).order_by('-this_total')

【问题讨论】:

    标签: python django aggregate-functions


    【解决方案1】:

    查看条件聚合:

    https://docs.djangoproject.com/en/1.10/ref/models/conditional-expressions/#conditional-aggregation

    items = (Widget.objects.filter(start__range=(prev_start, this_end))
             .values('dept')
             .annotate(prev_total=Count(Case(When(start__lte=prev_end, then='dept'), output_field=IntegerField())),
                       this_total=Count(Case(When(start__gte=this_start, then='dept'), output_field=IntegerField()))
                       )
             .order_by('-this_total'))
    

    【讨论】:

      【解决方案2】:

      您的想法似乎对您的情况是正确的。为了实现您的需要,您可以应用以下内容:

      1. range 查找功能虽小但很有帮助,可让您在两个值之间进行过滤
      2. 要限制annotate 参数,您需要使用conditional aggregation

      最后,您的查询将如下所示:

      items = Widget.objects.filter(start__range=(prev_start, this_end))
                            .values('dept')
                            .annotate(
                                 prev_total=Count(
                                     Case(
                                         When(start__lte=prev_end, then=1)),                 
                                         output_field=IntegerField())),
                                 this_total=Count(
                                     Case(
                                         When(start__gte=this_start, then=1), 
                                         output_field=IntegerField())))
                             .order_by('-this_total')
      

      因评论而编辑:

      在这种情况下不需要此步骤,这将有助于构建一个注释来对过滤后的 dept 字段的值求和。

      1. 我会使用F() 表达式来获取注解中每个dept 字段的值,并使用Sum 函数而不是Count 将这些值相加。

      【讨论】:

      • 谢谢,这绝对是正确的。它只是在第一个 annotate 之前缺少 .values('dept') 以获得我正在寻找的东西。另外,为什么使用F 表达式和Sum 而不是Count?由于链接文档中提到的竞争条件?
      • @JamieBull 我假设您需要将dept 值相加并得到它们的总和。如果不是这种情况,您只需要计算有多少 dept 值,那么我将编辑答案:)。同时,我将编辑答案中的values('dept')
      • 是的,它只是计算有多少部门值,所以我不需要F
      • @JamieBull 在这种情况下,您不需要F()。我将编辑我的答案以匹配您的情况。
      • 我看到的只是“签出条件聚合”部分和链接,所以去阅读它。当我回来时,有两个答案,其中一个得到了更好的解释并引用了当前的文档。我现在才意识到,我们最终得出的结果与您最初的答案相同,但增加了一些解释,我认为这对于将来到达这里的任何人来说都是更好的答案。
      猜你喜欢
      • 2016-05-24
      • 1970-01-01
      • 2017-09-16
      • 1970-01-01
      • 2022-06-29
      • 1970-01-01
      • 2021-12-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多