Django Querysets - 在.extra()之后使用.annotate()

时间:2015-02-27 11:46:26

标签: django django-queryset django-orm

让我们说我有一个非常简单的模型:

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'))

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

此外,如果没有创建一个存储派生信息的额外列(每个条目的持续时间),使用Django 1.8和/或使用原始SQL,那么您还可以推荐其他替代方案吗?非常感谢任何帮助。谢谢!

2 个答案:

答案 0 :(得分:2)

试试这个:

<击>

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

<击>

是的,它不会发生,因为

  

在调用values()之后进行的任何extra()调用将忽略其额外的选定字段。

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

  

如果在extra()调用之后使用values()子句,则extra()中的select参数定义的任何字段必须显式包含在values()调用中。

这样,任何.extra选择都会被包含in the grouping

但是你不能在没有Avg的情况下制作这种.extra,所以我认为唯一的解决办法是使用.raw

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

答案 1 :(得分:0)

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

from django.db.models import Avg, F

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