Django 查询集 - 在 .extra() 之后使用 .annotate()

Django Querysets - Using .annotate() after .extra()

假设我有一个像这样的非常简单的模型:

class Test(models.Model):
    category = models.CharField(unique=True)
    start_time = models.DateTimeField()
    end_time = models.DateTimeField()

基本上,我想使用 Django 的 ORM API 来获取每个类别的平均持续时间。 SQL 查询看起来像这样:

SELECT category, AVG(end_time - start_time) AS avg_duration
FROM test_table
GROUP BY category

我尝试使用以下代码,但根据文档,.annotate() 中的 F() 表达式仅在 Djano 1.8 中可用。

Test.objects.values('category', 'start_time', 'end_time').annotate(avg_duration=Avg(F(end_time) - F(start_time))

我也试过像这样使用 .extra(),但我得到了一个 FieldError。

Test.objects.extra(select={'duration':'end_time - start_time'}).values('category', 'duration').annotate(avg_duration=Avg('duration'))

从外观上看,第二次尝试表明注释函数无法解析列别名。真的是这样吗,还是我漏了什么?

此外,除了使用 Django 1.8 创建一个额外的列来存储派生信息(每个条目的持续时间),and/or 使用原始 SQL,你们还能推荐哪些其他替代方法?任何帮助将不胜感激。谢谢!

试试这个:

Test.objects.values('category').extra(select={'avg_duration':'AVG(end_time - start_time)'})

是的,这不会发生,因为

Any extra() call made after a values() call will have its extra selected fields ignored.

您也不能在 .values() 之前使用它,因为您将被迫将其包含在 .values():

If you use a values() clause after an extra() call, any fields defined by a select argument in the extra() must be explicitly included in the values() call.

所以这样任何 .extra select 都将被包含 in the grouping.

但是如果没有 .extra 你也不能做出这样的 Avg 所以我认为剩下的唯一解决方案是使用 .raw

Test.objects.raw('\
    SELECT id, category, AVG(end_time - start_time) AS avg_duration \
    FROM test_table \
    GROUP BY category'
)

如果您使用的是 Django 1.8 或更高版本,您可以使用如下内容:

from django.db.models import Avg, F

Test.objects.annotate(avg_duration=Avg(F('end_time') - F('start_time')))