Неправильный результат при аннотировании с помощью фильтра набора внешних ключей - PullRequest
0 голосов
/ 05 февраля 2019

Модель C имеет ссылку внешнего ключа на модель B. Модель B имеет ссылку внешнего ключа на модель A. Таким образом, экземпляр модели A может иметь много экземпляров модели B, а модель B может иметь много экземпляров модели C.

Псевдокод:

class A:
    ...

class B:
    a = models.ForeignKey(A, ...)

class C:
    b = models.ForeignKey(B, ...)

Я хотел бы получить весь список элементов модели A из базы данных, аннотируя его количеством элементов B, которое у них есть, соответственно их числуС элементов не является ни один (или любое другое случайное число, в этом отношении).

Я пробовал это:

A.objects.annotate(
    at_least_one_count=Count('b', filter=Q(b__c__isnull=False))
)

Насколько мне известно, это должно работать.Однако возвращенные числа не верны (в практическом случае).Он возвращает большее число, чем без фильтра, что, очевидно, невозможно:

A.objects.annotate(
    at_least_one_count=Count('b')
)

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

A.objects.first().b_set.filter(c__isnull=False).__len__()

Примечание: я хотел бы выполнить этот запрос без SQL.Если мне придется использовать некоторые более продвинутые инструменты Pythonic, которые предоставляет Django, я буду рад сделать это, пока я остаюсь объектно-ориентированным.Я пытаюсь отказаться от использования сырого SQL для всех операций с базами данных и переписать их все с помощью Django ORM.Однако это кажется слишком сложным.

1 Ответ

0 голосов
/ 05 февраля 2019

Ответ прост: после применения запросов, которые преобразуются в операторы объединения, необходимо выполнить отдельный фильтр, который в Django выполняется путем вызова .distinct(...) для набора запросов.

В этом случае, если вы используете фильтр, и для объекта фильтра есть отдельное ограничение, вы хотите использовать:

...=Count('b', filter=Q(b__c__isnull=False), distinct=True)
...