Django中日期时间字段之间的聚合差异

Posted

技术标签:

【中文标题】Django中日期时间字段之间的聚合差异【英文标题】:Aggregate difference between DateTime fields in Django 【发布时间】:2012-01-16 00:05:30 【问题描述】:

我有一个表格,其中包含一系列与时间段相关的条目(特别是为客户工作的时间):

task_time:
id     |    start_time    |    end_time       |    client (fk)
1        08/12/2011 14:48   08/12/2011 14:50     2

我正在尝试从我的 Django 应用汇总为给定客户端工作的所有时间:

time_worked_aggregate = models.TaskTime.objects.\
                        filter(client = some_client_id).\
                        extra(select = 'elapsed': 'SUM(task_time.end_time - task_time.start_time)').\
                        values('elapsed')

if len(time_worked_aggregate) > 0:
    time_worked = time_worked_aggregate[0]['elapsed'].total_seconds()
else:
    time_worked = 0

这看起来不优雅,但它确实有效。或者至少我是这么想的:事实证明它在 PostgreSQL 数据库上运行良好,但是当我转移到 SQLite 时,一切都死了。

一点挖掘表明,这样做的原因是DateTimes 不是 SQLite 中的一流数据。以下原始 SQLite 查询将完成我的工作:

SELECT SUM(strftime('%s', end_time) - strftime('%s', start_time)) FROM task_time WHERE ...;

我的问题如下:

上面的 Python 示例似乎是迂回的。我们可以更优雅地做到这一点吗? 更重要的是,在这个阶段,我们能否以一种既适用于 Postgres 又适用于 SQLite 的方式进行操作?理想情况下,我不想编写原始 SQL 查询并打开恰好到位的数据库后端;在一般中,Django 非常擅长保护我们免受这种情况的影响。 Django 对这个操作有合理的抽象吗?如果没有,我在后端进行条件切换的明智方法是什么?

我应该提到上下文,数据集有数千个条目;以下内容并不实际:

sum([task_time.end_date - task_time.start_date for task_time in models.TaskTime.objects.filter(...)])

【问题讨论】:

【参考方案1】:

几乎与@andri 提出的解决方案相同。在最终结果中,您将获得相同的数据。 ExpressionWrapper - New in Django 1.8.

from datetime import timedelta
from django.db.models import ExpressionWrapper, F, fields
from app.models import MyModel

duration = ExpressionWrapper(F('closed_at') - F('opened_at'), output_field=fields.DurationField())
objects = MyModel.objects.closed().annotate(duration=duration).filter(duration__gt=timedelta(seconds=2))

for obj in objects:
    print obj.id, obj.duration, obj.duration.seconds

# sample output
# 807 0:00:57.114017 57
# 800 0:01:23.879478 83
# 804 3:40:06.797188 13206
# 801 0:02:06.786300 126

【讨论】:

我认为您的回答并不准确,因为在时间增量计算的上下文中,我们不必处理不同的类型,ExpressionWrapper is necessary when using arithmetic on F() expressions with different typesdocs.djangoproject.com/en/1.8/ref/models/expressions/…【参考方案2】:

我认为从 Django 1.8 开始我们可以做得更好:

我只想绘制带有注释的部分,带有聚合的进一步部分应该很简单:

from django.db.models import F, Func
SomeModel.objects.annotate(
    duration = Func(F('end_date'), F('start_date'), function='age')
)

[更多关于 postgres 年龄函数的信息:http://www.postgresql.org/docs/8.4/static/functions-datetime.html]

SomeModel 的每个实例都将使用包含时间差的 duration 字段进行注释,在 python 中它将是一个 datetime.timedelta() 对象 [更多关于 datetime timedelta 的信息:https://docs.python.org/2/library/datetime.html#timedelta-objects]

【讨论】:

非常棒。谢谢。【参考方案3】:

我会一步一步做的:

    第一步:标注时间增量 按时间增量分组并求和

代码如下:

from django.db.models import Count, Sum, F

times_obj_list = models.TaskTime.objects.annotate(times=F("end_time")-F("start_time"))

groupby_obj_list = times_obj_list.values("client").annotate(cnt=Count("id"),seconds=Sum(times)).order_by()

【讨论】:

【参考方案4】:

Django 目前仅支持 Min、Max、Avg 和 Count 的聚合,因此使用原始 SQL 是实现您想要的唯一方法。当您使用原始 SQL 时,数据库独立性是不可能的,所以不幸的是,您不走运。您只需要检测数据库并适当地更改 SQL。

【讨论】:

不再是有效响应

以上是关于Django中日期时间字段之间的聚合差异的主要内容,如果未能解决你的问题,请参考以下文章

Django过滤列之间的日期差异

Elasticsearch日期之间的聚合字段

codeigniter中两个日期之间的差异

MySQL中日期之间的月份差异

在 SQL 中选择日期之间的平均差异

计算两个日期时间odoo 10之间的差异[重复]