在 Django 中按日期分组

Posted

技术标签:

【中文标题】在 Django 中按日期分组【英文标题】:Group by date in Django 【发布时间】:2021-11-24 11:15:17 【问题描述】:

我正在尝试实现以下 SQL 查询的结果

SELECT 
UNIX_TIMESTAMP(DATE((FROM_UNIXTIME(`timestamp`)))) AS `x`, 
COUNT(`timestamp`) as y 
    FROM somedb.events
        WHERE user_id=3 AND 
        `timestamp` > 1612117800 AND 
        `timestamp` < 1614450600 AND
        `kind`='food'
    GROUP BY `x`
    ORDER BY `x` desc;

使用 Django ORM。预期输出:

[
    
        "x": 1613759400,
        "y": 2
    ,
    
        "x": 1612463400,
        "y": 1
    
]

这是我目前尝试过的:

queryset = events.objects.filter(
            user=request.user,
            timestamp__range=dates
        )

第一种方法:

result = queryset.annotate(
            trunc_date_timestamp=Func(
              Value(
                TruncDate(
                    Func(
                        F('timestamp'),
                        function='FROM_UNIXTIME',
                    )
                )
              ),
              function='UNIX_TIMESTAMP',
              output_field=models.IntegerField()
            )
        ).values(x=F('trunc_date_timestamp')).annotate(y=models.Count('x')).order_by('-x')

这会产生输出:

[
    
        "x": 0,
        "y": 3
    
]

第二种方法:

result = queryset.annotate(
            trunc_date_timestamp=Func(
                    Func(
                        F('timestamp'),
                        function='FROM_UNIXTIME',
                        output_field=models.DateField()
                    ),
                function='UNIX_TIMESTAMP'
            )
        ).values(x=F('trunc_date_timestamp')).annotate(y=models.Count('x')).order_by('-x')

产生输出:

[
    
        "x": 1613831760,
        "y": 1
    ,
    
        "x": 1613810160,
        "y": 1
    ,
    
        "x": 1612520520,
        "y": 1
    
]

【问题讨论】:

【参考方案1】:

最后我让它工作了。我在这两种方法中都犯了一些小错误。

第一种方法:

result = ueryset.annotate(
            trunc_date_timestamp=Func(
                TruncDate(
                    Func(
                        F('timestamp'),
                        function='FROM_UNIXTIME',
                        output_field=models.DateField() # <<--- This is must
                    )
                ),
                function='UNIX_TIMESTAMP',
            )
        ).values(x=F('trunc_date_timestamp')).annotate(y=models.Count('x')).order_by('-x')

删除了Value() API 并将output_field=models.DateTimeField() 添加到内部Func() API。 output_field 将确保内部 Func() API 返回的字段必须是 models.DateField() 类型,TruncDate 将应用于该类型。

第二种方法:

queryset = queryset.annotate(
            trunc_date_timestamp=Func(
                Cast(
                    Func(
                        F('timestamp'),
                        function='FROM_UNIXTIME',
                    ),
                    output_field=models.DateField()
                ),
                function='UNIX_TIMESTAMP'
            )
        ).values(x=F('trunc_date_timestamp')).annotate(y=models.Count('x')).order_by('-x')

一开始我以为

Func(
    F('timestamp'),
    function='FROM_UNIXTIME',
    output_field=models.DateField()
)

这将返回一个models.DateField() 类型的字段,但是我不知道为什么它没有工作并且失败了!!! 所以我改用Cast() 方法并将返回的表达到models.DateField() 使其工作。

虽然这个解决方案有效,但我强烈认为第二种方法的原始代码也应该有效,因为如果我正确理解 Func() 表达式,那么两者都

Func(
   F('timestamp'),
   function='FROM_UNIXTIME',
   output_field=models.DateField()
) 

Cast(
    Func(
       F('timestamp'),
       function='FROM_UNIXTIME'
    ),
    output_field=models.DateField()
)

应该产生相同的结果。

【讨论】:

以上是关于在 Django 中按日期分组的主要内容,如果未能解决你的问题,请参考以下文章

在mysql和php中按日期范围分组

在 LINQ 中按特定列分组 [重复]

如何在 React Native 中按日期分组

如何在 MongoDB 中按日期分组

LINQ:在日期时间字段中按月和年分组

如何在 SQL 中按多列分组并按日期排序?