Django 查询集注释与日期时间计算

Posted

技术标签:

【中文标题】Django 查询集注释与日期时间计算【英文标题】:Django queryset annotate with datetime calculations 【发布时间】:2016-04-04 23:22:15 【问题描述】:

我的模型有两个日期时间,一个是实例创建日期,另一个是完成日期。

class Task(models.Model):
...
created = models.DateTimeField(default=timezone.now)

STATUS = Choices(
    ('draft', 'Draft'),
    ('open', 'Open'),
    ('wip', 'In Progress'),
    ('completed', 'Completed'),
)

datetime_started = models.DateTimeField(blank=True, null=True)
datetime_wip = models.DateTimeField(blank=True, null=True)
datetime_completed = models.DateTimeField(blank=True, null=True)
...

我正在尝试注释查询集以计算完成Task 所需的平均时间。我尝试使用 Django 的 AvgF 表达式,但无济于事:

today = timezone.now()
# We display the data of last 11 months + this month
tzinfo = timezone.get_current_timezone()
start_date = datetime(
    today.year,
    today.month,
    1,
    tzinfo=tzinfo) - relativedelta(months=11)

where = ("date_trunc('month', tasks_task.created AT TIME ZONE '%s')::date"
         % timezone.get_current_timezone_name())

tasks_last_12 = Task.objects.filter(
        created__range=(start_date, today),
    )
    .extra('month': where)
    .values('month')
    .order_by('month')
    .annotate(
        cmpl_time=Avg(F('datetime_completed') - F('datetime_started')),
    )
    .values_list('month', 'cmpl_time')

...

TypeError: float() argument must be a string or a number, not 'datetime.timedelta'

然后,我尝试使用SumCount 的组合来计算平均值,但这也不起作用:

tasks_last_12 = Task.objects.filter(
        created__range=(start_date, today),
    )
    .extra('month': where)
    .values('month')
    .order_by('month')
    .annotate(
        cmpl_time=Sum(
            (F('datetime_completed') - F('datetime_started')) / Count('datetime_completed')

        )
    )
    .values_list('month', 'cmpl_time'))

...

FieldError: Expression contains mixed types. You must set output_field

我该如何解决这个问题?

【问题讨论】:

【参考方案1】:

回复晚了,但是:

    您可能需要在 python 中减去日期,而不是在 ORM 中:date1 - date2 将给您一个 timedelta 对象。将这些相加并除以列表中的len。 尝试将 output_field 参数设置为您在 Sum(..., output_field=DateField()) 上的调用 cmpl_time 的计算应在除以Count 之前调用Sum 以计算平均值。

【讨论】:

【参考方案2】:

如果您使用的是 Postgres,那么您可以为此使用 django-pg-utils 包。将持续时间字段转换为秒,然后取平均值

from pg_utils import Seconds
from django.db.models import Avg

Task.objects.annotate(cmpl_time=Avg(Seconds(F('datetime_completed') - F('datetime_started'))))

【讨论】:

如果使用mysql呢?

以上是关于Django 查询集注释与日期时间计算的主要内容,如果未能解决你的问题,请参考以下文章

与 django 查询集注释中的先前对象的区别

Django:在请求值时从查询集注释字段中删除小数前缀

Django 日期时间查询

使用 django 查询返回活动时区中的日期时间

用日期时间注释 Django 查询集

Django 计算特定用户在日期时间字段中某个日期的访问次数