Django ORM 分组依据,并查找每组的最新项目(window 函数)

Django ORM group by, and find latest item of each group (window functions)

假设我们有如下模型

class Cake(models.Model):

    baked_on = models.DateTimeField(auto_now_add=True)
    cake_name = models.CharField(max_length=20)

现在,有多个 Cake 是在同一天烤制的,我需要一个查询来 return 我得到每月的蛋糕报告,其中包括每月的每一天,以及当天烘烤的第一个和最后一个蛋糕的名称。

例如,如果数据是这样的:

baked_on        cake_name
11 Jan 12:30    Vanilla
11 Jan 14:30    Strawberry
11 Jan 20:45    Avocado
12 Jan 09:05    Raspberry
12 Jan 16:30    Sprinkles
12 Jan 20:11    Chocolate

我的查询输出应该类似于

date    first     last
11 Jan  Vanilla   Avocado
12 Jan  Raspberry Chocolate

我应该如何在单个 ORM 调用中执行此操作?

Django 2.0 引入了 window 用于此类查询的函数。您的问题的简单答案是:

Cake.objects.annotate(
    first_cake=Window(
        expression=FirstValue('cake_name'),
        partition_by=[TruncDate('baked_on')],
        order_by=F('baked_on').asc(),
    ),
    last_cake=Window(
        expression=FirstValue('cake_name'),
        partition_by=[TruncDate('baked_on')],
        order_by=F('baked_on').desc(),
    ),
    day=TruncDate('baked_on'),
).distinct().values_list('day', 'first_cake', 'last_cake')

为什么 FirstValuelast_cake 中?这是因为 window 默认情况下查询将遍历每一行并且不会向前看,因此对于每一行,最后一行将等于当前行。使用 last_row 和降序排序将解决这个问题。或者你可以定义 window 查询应该工作的框架:

Cake.objects.annotate(
    first_cake=Window(
        expression=FirstValue('cake_name'),
        partition_by=[TruncDate('baked_on')],
        order_by=F('baked_on').asc(),
    ),
    last_cake=Window(
        expression=LastValue('cake_name'),
        partition_by=[TruncDate('baked_on')],
        order_by=F('baked_on').asc(),
        frame=ValueRange(),
    ),
    day=TruncDate('baked_on'),
).distinct().values_list('day', 'first_cake', 'last_cake')