django 博客使用 annotate 统计分类下文章数量

博客文章通常都有分类,有时候我们会看到分类名后面还跟着该分类下的文章数量。前面我们通过学习 django 博客开发入门教程搭建了一个小博客。现在想在现有的基础上实现统计分类下有多少篇文章,该怎么做呢?最优雅的方式就是使用 django 模型的 annotate 方法。

假设我们的 django 博客有一个 Post 和 Category 模型,分别表示文章和分类:

class Post(models.Model):
    title = models.CharField(max_length=70)
    body = models.TextField()
    category = models.ForeignKey('Category')

    def __str__(self):
        return self.title

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

我们知道从数据库取数据都是使用模型管理器 objects 实现的。比如获取全部分类是:Category.objects.all() ,假设有一个名为 test 的分类,那么获取该分类的方法是:Category.objects.get(name='test') 。objects 除了 all、get 等方法外,还有很多操作数据库的方法,而其中有一个 annotate 方法,该方法正可以帮我们实现本文所关注的统计分类下的文章数量的功能。具体来说,就是如下的代码:

from django.db.models.aggregates import Count
from blog.models import Category

# Count 计算分类下的文章数,其接受的参数为需要计数的模型的名称
category_list = Category.objects.annotate(num_posts=Count('post'))

这里 annotate 不仅从数据库获取了全部分类,相当于使用了 all 方法,它还帮我们为每一个分类添加了一个 num_posts 属性,其值为该分类下的文章数,这样我们在模板中就可以调用这个属性,例如:

{% for category in category_list %}
  <li>
    <a href="{% url 'blog:category' category.pk %}">
      {{ category.name }} ({{ category.num_posts }})</a>
  </li>
{% endfor %}

这样显示的效果就是分类名后跟着该分类下的文章数了。

那么 annotate 的工作原理究竟是怎么样的呢?在 Post 模型中我们通过 ForeignKey 把 Post 和 Category 关联了起来,这时候它们的数据库表结构就像下面这样:

Post 表:

id

title

body

category_id

1

post 1

...

1

2

post 2

...

1

3

post 3

...

1

4

post 4

...

2

Category 表:

name

id

category 1

1

category 2

2

这里前 3 篇文章属于 category 1,第 4 篇文章属于 category 2。

当 django 要查询某篇 post 对应的分类时,比如 post 1,首先查询到它分类的 id 为 1,然后 django 再去 Category 表找到 id 为 1 的那一行,这一行就是 post 1 对应的分类了。反过来,如果要查询 category 1 对应的全部文章呢?category 1 在 Category 表中对应的 id 是 1,django 就在 Post 表中搜索哪些行的 category_id 为 1,发现前 3 行都是,把这些行取出来就是 category 1 下的全部文章了。同理,这里 annotate 做的事情就是把全部 Category 取出来,然后去 Post 查询每一个 Category 对应的文章,查询完成后做一个聚合,统计每个 Category 有多少篇文章,把这个统计数字保存到 Category 的 num_posts 属性里(注意 Category 本身没有这个属性,是 Python 动态添加上去的)。

此外,annotate 方法不局限于用于本文提到的统计分类下的文章数,你也可以举一反三,只要是两个 model 类通过 ForeignKey 或者 ManyToMany 关联起来,那么就可以使用 annotate 方法来统计数量。比如下面这样一个标签系统:

class Post(models.Model):
    title = models.CharField(max_length=70)
    body = models.TextField()
    Tags = models.ManyToMany('Tag')

    def __str__(self):
        return self.title

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

统计标签下的文章数:

from django.db.models.aggregates import Count
from blog.models import Category

# Count 计算分类下的文章数,其接受的参数为需要计数的模型的名称
category_list = Category.objects.annotate(num_posts=Count('post'))

关于 annotate 方法官方文档的说明在这里:annotate。同时也建议了解了解 objects 下的其它操作数据库的方法,以便在遇到相关问题时知道去哪里查阅。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小樱的经验随笔

qsc oj 22 哗啦啦村的刁难(3)(随机数,神题)

哗啦啦村的刁难(3) 发布时间: 2017年2月28日 20:00   最后更新: 2017年2月28日 20:01   时间限制: 1000ms   内存限制...

2889
来自专栏瓜大三哥

移位寄存器的工作原理

把若干个触发器串接起来,就可以构成一个移位寄存器。由4个边沿D触发器构成的4位移位寄存器逻辑电路如图11-41所示。数据从串行输入端D1输入。左边触发器的输出作...

2228
来自专栏深度学习计算机视觉

pytorch demo 实践

相关环境 python opencv pytorch ubuntu 14.04 pytorch 基本内容 60分钟快速入门,参考:https://blog...

7366
来自专栏C#

开源免费的.NET图像即时处理的组件ImageProcessor

   承接以前的组件系列,这个组件系列旨在介绍.NET相关的组件,让大家可以在项目中有一个更好的选择,社区对于第三方插件的介绍还是比较少的,很多博文的内容主要还...

1070
来自专栏冷冷

利用iText 组件导出PDF

maven依赖:       <dependency>    <groupId>com.itextpdf</groupId>    <artifactId>...

2595
来自专栏点滴积累

geotrellis使用(十五)使用Bokeh进行栅格数据可视化统计

Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 实现方案 ...

3417
来自专栏WOLFRAM

Image Synthesis

1985
来自专栏C#

开源免费的.NET图像即时处理的组件ImageProcessor

   承接以前的组件系列,这个组件系列旨在介绍.NET相关的组件,让大家可以在项目中有一个更好的选择组件的介绍绝对不是一篇文章可以叙述完的,因为一个组件是经过开...

3008
来自专栏進无尽的文章

动画| 类似Windows的气泡屏保效果

有时候我们在打印一些CG类型的变量是,无法打印,利用UIKIT中的API可以很方便的实现 字符串和CG变量之间的转换。

3622
来自专栏深度学习之tensorflow实战篇

python下Matplotlib绘图案例与常见设置简介

首先一幅Matplotlib的图像组成部分介绍。 基本构成 在matplotlib中,整个图像为一个Figure对象。在Figure对象中可以包含一个或者多个A...

4496

扫码关注云+社区

领取腾讯云代金券