Могу ли я контролировать GROUP BY в Django 1.3? - PullRequest
5 голосов
/ 26 июля 2011

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

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

|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"

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

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

Ответы [ 2 ]

1 голос
/ 27 июля 2011

tl; dr: Django позволяет вам контролировать группу по выражению, но она ограничивает ее работу во всех разновидностях 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 на самом деле не должен позволять мне генерировать такой запрос, и я, по сути, пытаюсь решить мою проблему неправильно.

0 голосов
/ 27 июля 2011

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

YourModel.objects.values('this_is_your_group_by', 'even_a_second_field').annotate(sum=Sum('your_field'))
...