我有一个表格,其中包含一系列与时间段相关的条目(具体来说,是客户的工作时间):
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时,一切都会死掉。
有点挖掘表明其原因是DateTime
s不是SQLite中的第一类数据。以下原始SQLite查询将完成我的工作:
SELECT SUM(strftime('%s', end_time) - strftime('%s', start_time)) FROM task_time WHERE ...;
我的问题如下:
我应该提一下,数据集是数千个条目;以下是不切实际的:
sum([task_time.end_date - task_time.start_date for task_time in models.TaskTime.objects.filter(...)])
答案 0 :(得分:8)
几乎与@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
答案 1 :(得分:3)
我认为自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]
答案 2 :(得分:-1)
Django目前仅支持Min,Max,Avg和Count的聚合,因此使用原始SQL是实现您想要的唯一方法。当你使用原始SQL时,数据库独立性就在窗外,不幸的是,你运气不好。您必须只检测数据库并适当地更改SQL。