Могу ли я управлять GROUP BY в форме django 1.3?

Думаю, лучше всего это будет объяснить на примере.

Вот как будут выглядеть данные:

|project            |
|id|name            |
|1 |some project    |
|2 |my other project|

|run                                  |
|id|project_id|start_time   |result   |
|1 |1         |1305732581845|something|
|2 |1         |1305732593721|nothing  |
|3 |2         |1305732343721|nothing  |
|4 |2         |1305732556821|something|

Я хотел бы иметь возможность получить весь набор записей из каждого из последних запусков по проекту. SQL-запрос будет выглядеть примерно так:

SELECT *, MAX("run"."start_time")
FROM "run"    
LEFT OUTER JOIN "project" ON ("run"."project_id" = "project"."id") 
GROUP BY "project"."id"

Это вернет мне все столбцы в обеих таблицах для последнего запуска проекта, и это здорово, это именно то, что мне нужно.

Поэтому, пытаясь найти эквивалент django orm в django 1.3, я просто не могу найти правильный способ сделать это. Если я сделаю что-то вроде этого:

Run.objects.annotate(Max('start_time'))

Сгенерированный SQL-запрос будет выглядеть примерно так:

SELECT 
"run"."id", "run"."result", "run"."project_id", "project"."id", "project"."name", 
MAX("run"."start_time")
FROM "run"
LEFT OUTER JOIN "project" ON ("run"."project_id" = "project"."id")
GROUP BY "run"."id", "run"."result", "run"."project_id", "project"."id", "project"."name"

Это не вернет мне правильные результаты, так как группа неверна для того, что я хочу. Я считаю, что в предыдущих версиях django следующее правильно и явно устанавливало предложение group by в запросе, но, похоже, не работает в 1.3:

q = Run.objects.annotate(Max('start_time'))
q.query.group_by = [("project", "id")]

В версии 1.3 это создает точно такой же запрос, как если бы свойство group_by не изменялось вручную в запросе.

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

q = Run.objects.values('project__id').annotate(Max('start_time')).values('id')

Я закончил с таким запросом:

SELECT 
"run"."id", "run"."project_id"
MAX("run"."start_time")
FROM "run"
LEFT OUTER JOIN "project" ON ("run"."project_id" = "project"."id")
GROUP BY "run"."id", "run"."project_id"

Может ли кто-нибудь указать мне правильный способ делать то, что я делаю, без каких-либо из следующих действий:

  • Использование необработанного sql - какой смысл использовать форму, когда мне постоянно приходится генерировать свои собственные запросы?
  • Использование .extra(select = {'latest': 'somequery'}) - зачем мне использовать подзапросы, когда вполне допустимый запрос без подзапросов может дать мне то, что я хочу.
  • Использование нескольких запросов для получения одних и тех же данных — опять же, зачем мне делать несколько запросов, чтобы получить результаты, доступные в 1?

person mockobject    schedule 26.07.2011    source источник


Ответы (2)


Вкратце: Django позволяет вам управлять предложением group by, но ограничивает его работу со всеми разновидностями SQL, поэтому я не могу делать то, что хочу.

Мне было указано, что исходный запрос, который я пытаюсь сгенерировать с помощью ORM django, на самом деле не действителен для всех разновидностей SQL. Вот обновление запроса, который я искал:

SELECT *, MAX("run"."start_time")
FROM "run"    
LEFT OUTER JOIN "project" ON ("run"."project_id" = "project"."id") 
GROUP BY "project"."id"

Если человек попытается выбрать что-то, чего нет в GROUP BY в MSSQL, он на самом деле получит сообщение об ошибке. Поэтому мне кажется, что django на самом деле не должен позволять мне генерировать такой запрос, и я, по сути, пытаюсь решить свою проблему неправильно.

person mockobject    schedule 27.07.2011

Это довольно просто и подробно описано в разделе аннотации документов, и ни в одной из предыдущих версий вы не могли вручную установить группу.

YourModel.objects.values('this_is_your_group_by', 'even_a_second_field').annotate(sum=Sum('your_field'))
person John    schedule 26.07.2011
comment
Это не вернет весь набор записей для каждой строки. Это даст мне только значения из столбцов в списке значений. Это означает, что я не могу получить данные из столбцов без группировки по столбцам, и, к сожалению, мне нужны идентификаторы прогонов. - person mockobject; 27.07.2011
comment
`values('groupings,...).annotate().values('more_fields_to_show_here') Убедитесь, что вы добавили имя поля, добавленного аннотацией, к термину значений после аннотации. Вам действительно следует прочитать docs.djangoproject.com/en/1.3/ темы/БД/агрегация/ - person John; 27.07.2011
comment
Я забыл упомянуть, что это был первый метод, который я попробовал, потому что, судя по документации, это казалось логичным. Однако это не сработало, как ожидалось. Все значения в последнем вызове значений попадают в предложение group_by. Я прочитал документацию, она просто не работает должным образом, если только я не делаю с ней что-то очень неправильное. Я обновлю исходный вопрос, чтобы отметить, что я пробовал это и каковы были результаты, это был просто недосмотр, когда я писал вопрос. - person mockobject; 27.07.2011