Не могу сделать несколько аннотаций с Джанго
Я работаю с django-eztables, чтобы выполнить обработку данных на стороне сервера, и я хочу добавить некоторые поля, включающие агрегацию ( как упомянуто здесь)
Следующие каждый работает отлично индивидуально:
def get_queryset(self):
qs = super(SomeObjectDataTableView, self).get_queryset()
return qs.select_related().annotate(items_count=Count('items'))
А также
def get_queryset(self):
qs = super(SomeObjectDataTableView, self).get_queryset()
return qs.select_related().annotate(total_sum=Sum('anotherobject__differentobject__total'))
но если я попытаюсь сделать оба, в любом порядке, в том же annotate()
или прикованные друг к другу, оба они заканчиваются тем же номером, который является результатом того, кем они должны быть.
Если я добавлю distinct=True
для счетчика он выдает правильное значение, но все равно заставляет сумму давать завышенное значение. (Опять же, изменение порядка не помогает, и сумма не занимает distinct
параметр)
Я видел пару похожих вопросов по SO, но большинство, казалось, имели дело с несколькими подсчетами, которые можно решить с помощью distinct=True
, Был тот, у которого была сумма, но решение включало использование extra()
и некоторый SQL, созданный вручную, который я до сих пор не смог приспособить для работы со всеми необходимыми мне обходами внешнего ключа (я немного использовал SQL, но я ни в коем случае не эксперт). Вот основные настройки соответствующих моделей, на случай extra()
является единственным жизнеспособным решением:
- Элемент имеет внешний ключ для SomeObject
- AnotherObject имеет внешний ключ для DifferentObject и внешний ключ для SomeObject
Если кто-нибудь знает, как мне обойти эти проблемы и получить обе аннотации в моем наборе запросов, это будет очень цениться.
1 ответ
Django непонятно, как переводятся отношения в SQL... и SQL обычно объясняет основную проблему в выводе Django. Возьмите структуру данных как:
Таблица для взрослых (Имя)
- боб
- ...
Детский стол (родитель, ребенок)
- Боб, Джон
- Боб, Стейси
- ...
Pet Table (Владелец, Pet)
- Боб, Кот
- Боб, Собака
Допустим, вы хотите стол с
- название
Дети
Домашние питомцы
Аннотации достигаются с помощью JOIN. Если вы попытаетесь аннотировать с двумя счетами, Django выполнит следующий запрос (псевдо SQL, если читатели не знают SQL):
SELECT Adult's Name, Count of Children, Count of Pets
FROM Adult
JOIN Child (where the adult is the parent)
JOIN Pet (where the adult is the owner)
К сожалению, это приведет к следующим строкам для Боба:
Bob, John, Cat
Bob, John, Dog
Bob, Stacy, Cat
Bob, Stacy, Dog
Если вы просто посчитаете Child и Pet, вы получите произведение двух отсчетов (4 = 2 x 2). В примерах, которые вы видели, это решается включением значения DISTINCT в каждый счетчик для устранения дубликатов.
В вашем случае вместо четко названного объекта, такого как домашние животные, вы собираете итоги по связанному объекту:
Bob, John, 250
Bob, John, 100
Bob, Stacy, 250
Bob, Stacy, 100
Даже если бы вы могли заставить DISTINCT работать в простом случае, это было бы чрезвычайно опасно, потому что итоги по двум связанным объектам могли бы быть абсолютно одинаковыми. Вызов DISTINCT устранит одно из двух значений, даже если оба они действительны. Технически, этот подход может также сломаться с именованным значением. Например, если вы хотите посчитать количество домашних животных на улице, DISTINCT может дать неправильный ответ, если два из этих домашних животных имеют одинаковое имя.
Непосредственно добиться желаемого результата сложно в Django, потому что это сложно в SQL:
- Самый простой подход состоит в том, чтобы выполнить полный запрос, но разделить SUM на COUNT при использовании значения. Вы можете понять, почему это работает в приведенном выше примере... так как для каждого экземпляра другого подсчитанного значения будет указана вся сумма.
- Как этого добиться, зависит от вашей ситуации:
- Я думаю, что вы хотите использовать extra(). Ответ на этот вопрос показывает, как объединить два агрегатных значения, используя extra () и не используя необработанный SQL. Я предполагаю (но не проверял), что вы могли бы также легко разделить сумму на счет.
- Вы можете вручную перебрать значения и запустить второй запрос. Поскольку вы используете Datatables, я предполагаю, что это невозможно.
- В зависимости от вашей структуры Datatable, вы также можете сделать это при определении или отображении Datatable.
Это не может быть исчерпывающим, но первое довольно прямолинейно, поэтому я не стал придумывать других.