【问题标题】:How to filter Django annotations on reverse foreign key fields如何过滤反向外键字段上的 Django 注释
【发布时间】:2019-02-28 05:19:10
【问题描述】:

我正在尝试获取具有特定字段值的所有相关模型的计数。

这是一些代码...

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

但这些都不起作用。

有什么建议吗?

【问题讨论】:

  • 你用的是什么 Django 版本?
  • @WillemVanOnsem - 1.11

标签: django django-aggregation


【解决方案1】:

你需要.filter(..)之前.annotate(..),所以:

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

这将导致Authors 中的QuerySet,其中每个Author 都有一个额外的属性.n_good_books,其中包含好的Books 的数量。相反意味着您会检索到至少一个相关的Book 已获得好评的Authors。和specified in the documentation一样:

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(..)) 方法仅在 之后才有效,因此在 中这将有效。

【讨论】:

  • 有没有办法一次性为 each 书评类型添加注释,所以我最终会计算“n_good_books”、“n_bad_books”、 “n_great_books”等?
  • @trubliphone:是的,但它非常不雅。我建议升级到 Django-2.0,这样您就可以使用 Count(..,filter=...) 方法。
【解决方案2】:

@willem-van-onsem 对我提出的问题有正确答案。

然而,如果我想一次计算所有书籍类型的数量,我可以这样做:

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())),
)

他是对的,这很不雅。

【讨论】:

    猜你喜欢
    • 2021-12-24
    • 1970-01-01
    • 2014-08-24
    • 2019-01-22
    • 1970-01-01
    • 2021-07-02
    • 2020-09-23
    • 2020-03-11
    • 1970-01-01
    相关资源
    最近更新 更多