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

ИТЬ

Благодаря опубликованному ответу я нашел гораздо более простой способ сформулировать проблему. Оригинальный вопрос можно увидеть в истории изменений.

Эта проблема

Я пытаюсь перевести запрос SQL на Django, но получаю ошибку, которую не понимаю.

Вот модель Django, которую я имею:

class Title(models.Model):
  title_id = models.CharField(primary_key=True, max_length=12)
  title = models.CharField(max_length=80)
  publisher = models.CharField(max_length=100)
  price = models.DecimalField(decimal_places=2, blank=True, null=True)

У меня есть следующие данные:

publisher                    title_id      price  title
---------------------------  ----------  -------  -----------------------------------
New Age Books                PS2106         7     Life Without Fear
New Age Books                PS2091        10.95  Is Anger the Enemy?
New Age Books                BU2075         2.99  You Can Combat    Computer Stress!
New Age Books                TC7777        14.99  Sushi, Anyone?
Binnet & Hardley             MC3021         2.99  The Gourmet Microwave
Binnet & Hardley             MC2222        19.99  Silicon Valley   Gastronomic Treats
Algodata Infosystems         PC1035        22.95  But Is It User Friendly?
Algodata Infosystems         BU1032        19.99  The Busy Executive's   Database Guide
Algodata Infosystems         PC8888        20     Secrets of Silicon Valley

Вот что я хочу сделать: ввести аннотированное полеdbl_price что вдвое превышает цену, затем сгруппируйте полученный набор запросов поpublisherи для каждого издателя вычислите сумму всехdbl_price значения для всех названий, опубликованных этим издателем.

SQL-запрос, который делает это, выглядит следующим образом:

SELECT SUM(dbl_price) AS total_dbl_price, publisher
FROM (
  SELECT price * 2 AS dbl_price, publisher
  FROM title
) AS A 
GROUP BY publisher

Желаемый результат будет:

publisher                    tot_dbl_prices
---------------------------  --------------
Algodata Infosystems                 125.88
Binnet & Hardley                      45.96
New Age Books                         71.86 
Джанго запрос

Запрос будет выглядеть так:

Title.objects
 .annotate(dbl_price=2*F('price'))
 .values('publisher')
 .annotate(tot_dbl_prices=Sum('dbl_price'))

но выдает ошибку:

KeyError: 'dbl_price'. 

что указывает на то, что он не может найти полеdbl_price в наборе запросов.

Причина ошибки

Вот почему эта ошибка происходит:документация говорит

Следует также отметить, что средняя_категория была явно включена в список значений, которые должны быть возвращены. Это необходимо из-за упорядочения предложений values ​​() и annotate ().

Если предложение values ​​() предшествует предложению annotate (), любые аннотации будут автоматически добавлены в набор результатов. Однако, если предложение values ​​() применяется после предложения annotate (), необходимо явно включить столбец агрегирования.

Итакdbl_price не удалось найти в агрегации, поскольку он был созданannotate, но не был включен вvalues().

Тем не менее, я не могу включить его вvalues либо, потому что я хочу использоватьvalues (сопровождаемый другимannotate) как группирующее устройство, так как

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

которая является основой того, как Джангореализует SQLGROUP BY, Это означает, что я не могу включитьdbl_price внутриvalues()потому что тогда группировка будет основываться на уникальных комбинациях обоих полейpublisher а такжеdbl_priceв то время как мне нужно сгруппировать поpublisher только.

Итак, следующий запрос, который отличается от приведенного выше только тем, что я агрегирую по моделямprice поле, а не аннотированныйdbl_price поле, на самом деле работает:

Title.objects
 .annotate(dbl_price=2*F('price'))
 .values('publisher')
 .annotate(sum_of_prices=Count('price'))

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

Вопрос

Итак, у нас есть это: мне нужно включить аннотированное свойство вvalues чтобы сохранить его в наборе запросов, но я не могу этого сделать, потому чтоvalues также используется для группировки (что будет неправильно с дополнительным полем). Проблема, по сути, связана с двумя очень разными способамиvalues используется в Django, в зависимости от контекста (независимо от того,values сопровождаетсяannotate) - который является (1) извлечением значения (обычный SQL)SELECT список) и (2) группировка + агрегирование по группам (SQLGROUP BY) - и в этом случае эти два способа кажутся противоречивыми.

Мой вопрос: есть ли способ решить эту проблему (без таких вещей, как возврат к raw SQL)?

Пожалуйста, обратите внимание: конкретный пример может быть решен путем перемещения всехannotate заявления послеvalues, который был отмечен несколькими ответами. Тем не менее, меня больше интересуют решения (или обсуждение), которые быannotate заявление передvalues()По трем причинам: 1. Существуют также более сложные примеры, где предложенный обходной путь не будет работать. 2. Я могу представить себе ситуации, когда аннотированный набор запросов был передан другой функции, которая фактически выполняет GROUP BY, так что единственное, что мы знаем, это набор имен аннотированных полей и их типы. 3. Ситуация кажется довольно простой, и меня удивило бы, если бы это столкновение двух разных видов использованияvalues() не было замечено и обсуждено прежде.

Ответы на вопрос(4)

Ваш ответ на вопрос