【问题标题】:Group by date in Django在 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
    }
]

【问题讨论】:

    标签: python django group-by django-orm django-annotate


    【解决方案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()
    )
    

    应该产生相同的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-02-03
      • 2023-03-18
      • 1970-01-01
      • 2020-09-09
      • 2016-03-08
      • 2018-06-26
      • 2011-03-01
      相关资源
      最近更新 更多