2015-02-27 2 views
3

Допустим, у меня есть очень простая модель, как этот:Джанго Querysets - Использование .annotate() после .extra()

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

В принципе, я хочу использовать ORM API Django, чтобы получить среднюю продолжительность по категории. Запрос SQL будет выглядеть примерно так:

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

Я попытался, используя следующий код, но в соответствии с Документами, F() выражения в .annotate() доступны только в 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 и/или с использованием необработанного SQL, какие другие альтернативы вы можете рекомендовать? Любая помощь будет очень оценена. Благодаря!

ответ

2

Попробуйте это:

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

Да, это не произойдет, потому что

Любое дополнительное() прозвучавший после значений() вызов будет игнорировать его дополнительные выбранные поля.

Вы не можете использовать его, прежде чем .values() либо, потому что вы будете вынуждены включить его в .values():

Если вы используете значения() пункта после того, как дополнительный() вызова, любой поля, определенные аргументом select в extra(), должны быть явно включены в вызов values ​​().

так таким образом любой .extra select будет включен 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' 
) 
+0

Я попробовал ваше предложение, а также попробовал это, опасаясь, что требуемые столбцы для .extra() могут не быть обнаружены. 'Test.objects.values ​​('category', 'end_time', 'start_time').экстра (выберите = { 'avg_duration': 'AVG (END_TIME - start_time)'}) ' Результаты, которые я получил, был похож на основной запрос выбора, как это: ' Выберите категорию, END_TIME, start_time ИЗ test_table' Ницца предложение, но я думаю, что именно поэтому он не сработал: «Любой вызов extra(), сделанный после вызова values ​​(), будет игнорировать его дополнительные выбранные поля». Из [Django docs] (https://docs.djangoproject.com/en/1.7/ref/models/querysets/#values) –

+0

Да, вы правы. Я играл некоторое время, когда оболочка тестировала различные варианты, но не могла найти решение, отличное от '.raw'; ( – Todor

+0

Я очень ценю предложения и усилия! Я буду отмечать это как ответ на благо других, использующих Django 1.7. –

0

Если вы используете Django 1.8 или выше, вы могли бы использовать что-то вот так:

from django.db.models import Avg, F 

Test.objects.annotate(avg_duration=Avg(F('end_time') - F('start_time'))) 
Смежные вопросы