Django使用annotate对DurationField求和并转换为小数小时
Django DurationField求和后转十进制小时实现方案
你当前写法有个细节问题:如果要对全表所有记录的Time字段求总和,应该用aggregate()而不是annotate()——annotate()是给每一行查询结果追加聚合字段,aggregate()才是直接返回全表聚合后的单值结果。
两种实现方式按需选择:
方案1:数据库层直接计算返回小时值(推荐)
直接在ORM查询阶段完成时间转小时的计算,查询返回的结果直接是你要的十进制小时数,不需要后续额外转换:
from datetime import timedelta from django.db.models import Sum from django.db.models.functions import Extract # 聚合查询,同时返回原始总时长、十进制小时数 result = Task.objects.aggregate( total_timedelta=Sum('Time', default=timedelta(0)), total_hours=Extract(Sum('Time', default=timedelta(0)), 'epoch') / 3600 )
代码说明:
Extract(聚合结果, 'epoch')会把Sum计算得到的时间间隔直接转为总秒数- 总秒数除以3600即可得到十进制小时值,比如总秒数为5400时,计算结果就是1.5,完全匹配你的预期
- 给
Sum传入default=timedelta(0)是为了避免表内无数据、或存在Time字段为NULL的记录时返回None导致计算报错
方案2:Python层转换timedelta结果
如果你已经通过Sum拿到了timedelta类型的总时长,直接调用timedelta内置方法计算即可:
from datetime import timedelta from django.db.models import Sum # 拿到总时长timedelta对象 total_td = Task.objects.aggregate(total=Sum('Time', default=timedelta(0)))['total'] # 转十进制小时 total_hours = total_td.total_seconds() / 3600
比如total_td为timedelta(seconds=5400)时,total_td.total_seconds()会返回5400,除以3600后得到1.5。
内容的提问来源于stack exchange,提问作者MichaelS
相关产品推荐
相关产品推荐

