Django ORM中.count()的正确使用时机

The right time to use .count() in Django ORM

这些是我在我的小项目中的模型

class Tag(models.Model):
    name = models.CharField(max_length=200)

class Post(models.Model):
    content = models.CharField(max_length=200)
    tags = models.ManyToManyField(Tag)
    location = models.PointField(null=True, blank=True)

给我一个标签列表(最多3个)。我需要找到每个标签出现在 Post 上的次数。让我们说 ["funny","geek","tech"]

我正在做的是获取标签计数

    for tag in tags:
        tag_data[tag] = Post.objects.filter(
            tags__name=tag,
            location__distance_lte=(
                pnt, D(km=distance))).count()

然后我使用以下查询获取所有 post 数据。

    posts = Post.objects.filter(
             tags__name__in=tags,
             location__distance_lte=(pnt, D(km=distance)))

我已经在函数结束时循环 posts。

for post in posts:
   #some logic

所以我知道那里有标签数据。无需像上面那样进行单独的数据查询来查找计数。但是我发现如果我想在 for 循环中找到标签的计数,我需要在 for 循环中再添加 1 个 for 循环。

for post in posts:
   #some logic
   for tag in post.tags:
      #some logic

所以我的问题是在这里哪个更有效率。像我一样使用 .count() 获取或嵌套 for 循环。

在 Django ORM 中有一个名为 Aggregation 的东西,它使用 .annotate().aggregate() 方法将您正在寻找的功能添加到查询。 (Docs on Aggregation).

有很多例子可以说明如何获得 CountAvgSum 和 more.This 也可以在许多地方完成-对多关系就像你的情况一样。

There's even an example 在与您的情况非常相似的文档中。

For example, if you are retrieving a list of books, you may want to know how many authors contributed to each book. Each Book has a many-to-many relationship with the Author; we want to summarize this relationship for each book in the QuerySet.

要计算 Post 中出现的 Tag 的数量,您只需这样做:

# Add any arguments you want to the filter() to narrow down the Tag query set.
tags = Tags.objects.filter().annotate(Count('post_set'))
# You access it as post_set__count

编辑:

根据您提供的评论,您希望向注释和查询添加条件,以便能够仅对半径内的帖子进行计数。为此,您可以使用 Django 调用的 Conditional Expressions.

借助条件表达式,您可以向 .annotate() 添加条件。

示例:

tags = Tags.objects.filter().annotate(Count(
        Case(When(post_set__location="1", then=1), output_field=IntegerField())
))

请阅读文档以找到适合您的情况的正确使用方法。你可以看到 Conditional Expressions with Aggregations.