Как отфильтровать аннотации Django по полям обратного внешнего ключа - PullRequest
0 голосов
/ 24 сентября 2018

Я пытаюсь получить счетчик всех связанных моделей с определенным значением поля.

Вот код ...

models.py:

class Author(models.Model):
  name = models.CharField(max_length=100)

class Book(models.Model):
  BAD = "BAD"
  MEH = "MEH"
  GOOD = "GOOD"
  GREAT = "GREAT"
  REVIEW_CHOICES = (
    (BAD, BAD.title()),
    (MEH, MEH.title()),
    (GOOD, GOOD.title()),
    (GREAT, GREAT.title()),
  )
  title = models.CharField(max_length=100)
  review = models.CharField(max_length=100, choices=REVIEW_CHOICES)
  author = models.ForeignKey(Author, related_name="books")

Предположим, я хочу перечислить количество обзоров каждого типа для каждого автора.

Я пробовал:

Authors.object.annotate(n_good_books=Count("books")).filter(books__review="GOOD").values("name", "n_good_books")  

Я также пробовал:

Authors.object.annotate(n_good_books=Count("books", filter=Q(books_review="GOOD"))).values("name", "n_good_books")  

Но ни одна из этих работ.

Есть предложения?

Ответы [ 2 ]

0 голосов
/ 25 сентября 2018

@ Виллем-ван-онсем имеет правильный ответ на вопрос, который я задал.

Однако , если бы я хотел получить счет для всех типов книг одновременно, я мог бы сделать что-то вроде:

from django.db.models import Case, When, IntegerField

Authors.object.annotate(
  n_bad_books=Count(Case(When(books__review="BAD", then=1), output_field=IntegerField())),
  n_meh_books=Count(Case(When(books__review="MEH", then=1), output_field=IntegerField())),
  n_good_books=Count(Case(When(books__review="GOOD", then=1), output_field=IntegerField())),
  n_great_books=Count(Case(When(books__review="GREAT", then=1), output_field=IntegerField())),
)

И он прав, это оченьбезвкусный.

0 голосов
/ 24 сентября 2018

Вам необходимо .filter(..) до .annotate(..), поэтому:

Authors.object.filter(
    <b>books__review="GOOD"</b>  # before the annotate
).annotate(
    n_good_books=Count("books")
)

Это приведет к QuerySet из Author с, где каждый Author имеет дополнительный атрибут .n_good_books, который содержит количество хороших Book с.Противоположное означает, что вы only получите Author s, для которых как минимум один связанный Book получил хороший отзыв.Как указано в документации :

Когда используется с annotate() предложением , фильтр имеет эффект , ограничивающий объекты длякоторый аннотация рассчитывается .Например, вы можете создать аннотированный список всех книг с заголовком, начинающимся с «Django», используя запрос:

>>> from django.db.models import Count, Avg
>>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))

(..)

Аннотированные значения могуттакже будет отфильтровано .Псевдоним для аннотации можно использовать в предложениях filter() и exclude() так же, как и в любом другом поле модели.

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

>>> Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)

Этот запрос генерирует аннотированный набор результатов, а затем создает фильтр на основе этой аннотации.

Подход Count(..., filter=Q(..)) работает только с , поэтому в это будет не работать.

...