【问题标题】:How to write a Django QuerySet the properly computes average of DateTimeField with grouping?如何编写 Django QuerySet 以正确计算 DateTimeField 的平均值和分组?
【发布时间】:2015-04-29 18:57:29
【问题描述】:

这是我的 Django 模型:

class MyModel(models.Model):
    a = IntegerField()
    b = DateTimeField()

这是我在此模型上执行的查询集,用于查找 bs 的每个 a 值的计数、最小值、最大值和平均值:

>>> from django.db.models import Count, Max, Min, Avg
>>> MyModel.objects.extra(
...    select={'avg': 'AVG(UNIX_TIMESTAMP(b))'}
... ).values('a').annotate(
...     count=Count('b'), 
...     min=Min('b'), 
...     max=Max('b'),
... )

这是上面 QuerySet 的结果:

[
  {'a': 1, 'count': 5, 'min': datetime.datetime(2015, 2, 26, 1, 8, 21, tzinfo=<UTC>), 'max': datetime.datetime(2015, 2, 26, 1, 8, 22, tzinfo=<UTC>)}, 
  {'a': 2, 'count': 2, 'min': datetime.datetime(2015, 2, 26, 1, 8, 21, tzinfo=<UTC>), 'max': datetime.datetime(2015, 2, 26, 1, 8, 22, tzinfo=<UTC>)}
]

如您所见,QuerySet 的结果不包括我计算的平均字段。我怎样才能把它弄进去?我尝试了许多不同的排列。但是,如果我可以在其中获得avg 字段,那么它似乎会破坏a 的分组。

【问题讨论】:

标签: python django django-models django-queryset


【解决方案1】:

您可以使用raw sql query,而不是使用 Django ORM。

from django.db import connection
query = """
SELECT `a`,
       COUNT(b) AS `count`,
       MAX(b) AS `max`,
       AVG(UNIX_TIMESTAMP(b)) AS `avg`,
       MIN(b) AS `min`
FROM `<appname>_<modelname>`
GROUP BY `a`
"""
cursor = connection.cursor()
cursor.execute(query)
result = cursor.fetchall()

这会给你一些类似的东西:

(
    (a value1, count, max, avg as unix timestamp, min), ...
    (a value2, count, max, avg as unix timestamp, min), ...
    (a value3, count, max, avg as unix timestamp, min), ...
)

否则,使用 Django 的 ORM 最接近的方法是放弃额外子句中的 UNIX_TIMESTAMP 转换:

from django.db.models import Count, Min, Max, Avg
MyModel.objects.all().values('a').annotate(
    count=Count('b'), 
    max=Max('b'), 
    avg=Avg('b'), 
    min=Min('b')
)

不幸的是,这会给你average as a float

[
    {
        'count': 15366, 
         'a': 0, 
         'avg': 19898862327498.82, 
         'min': datetime.datetime(1900, 1, 1, 0, 0), 
         'max': datetime.datetime(2012, 7, 3, 0, 0)
    }, {
         'count': 1726, 
         'a': 1, 
         'avg': 19785827400927.0, 
         'min': datetime.datetime(1920, 8, 25, 0, 0), 
         'max': datetime.datetime(1994, 12, 29, 0, 0)
    }, 
    ...

]

您可以尝试使用类似的东西

import datetime
datetime.datetime.strptime(str(int(ts)), '%Y%m%d%H%M%S%f')

将其转换回日期时间对象,虽然这将是一个近似值,所以我建议改用原始 sql。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-25
    • 2013-04-26
    • 2021-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多