统计各个分类和标签下的文章数

作者:HelloGitHub-追梦人物

文中所涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库

在我们的博客侧边栏有分类列表和标签列表,显示博客已有的全部文章分类。现在想在分类名和标签名后显示该分类或者标签下有多少篇文章,该怎么做呢?最优雅的方式就是使用 django 的 annotate 方法。

Model 回顾

回顾一下我们的 model 代码,django 博客有一个 PostCategory 模型,分别表示文章和分类:

blog/models.py

class Post(models.Model):
    title = models.CharField('标题', max_length=70)
    body = models.TextField('正文')
    category = models.ForeignKey(Category, verbose_name='分类', on_delete=models.CASCADE)
    # 其它属性...

    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 除了 allget 等方法外,还有很多操作数据库的方法,而其中有一个 annotate 方法,该方法正可以帮我们实现本文所关注的统计分类下的文章数量的功能。

数据库数据聚合

annotate 方法在底层调用了数据库的数据聚合函数,下面使用一个实际的数据库表来帮助我们理解 annotate 方法的工作原理。在 Post 模型中我们通过 ForeignKeyPostCategory 关联了起来,这时候它们的数据库表结构就像下面这样:

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 id 对应有多少行记录,这样就可以统计出每个 Category 下有多少篇文章了。把这个统计数字保存到每一条 Category 的记录就可以了(当然并非保存到数据库,在 Django ORM 中是保存到 Category 的实例的属性中,每个实例对应一条记录)。

使用 Annotate

以上是原理方面的分析,具体到 Django 中该如何用呢?在我们的博客中,获取侧边栏的分类列表的方法写在模板标签 get_categories 里,因此我们修改一下这个函数,具体代码如下:

blog/templatetags/blog_extras.py

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

@register.inclusion_tag('blog/inclusions/_categories.html', takes_context=True)
def show_categories(context):
    category_list = Category.objects.annotate(num_posts=Count('post')).filter(num_posts__gt=0)
    return {
        'category_list': category_list,
    }

这个 Category.objects.annotate 方法和 Category.objects.all 有点类似,它会返回数据库中全部 Category 的记录,但同时它还会做一些额外的事情,在这里我们希望它做的额外事情就是去统计返回的 Category 记录的集合中每条记录下的文章数。代码中的 Count 方法为我们做了这个事,它接收一个和 Categoty 相关联的模型参数名(这里是 Post,通过 ForeignKey 关联的),然后它便会统计 Category 记录的集合中每条记录下的与之关联的 Post 记录的行数,也就是文章数,最后把这个值保存到 num_posts 属性中。

此外,我们还对结果集做了一个过滤,使用 filter 方法把 num_posts 的值小于 1 的分类过滤掉。因为 num_posts 的值小于 1 表示该分类下没有文章,没有文章的分类我们不希望它在页面中显示。关于 filter 函数以及查询表达式(双下划线)在之前已经讲过,具体请参考 分类、归档和标签页

同理,tags 也可以做同样的操作。

@register.inclusion_tag('blog/inclusions/_tags.html', takes_context=True)
def show_tags(context):
    tag_list = Tag.objects.annotate(num_posts=Count('post')).filter(num_posts__gt=0)
    return {
        'tag_list': tag_list,
    }

在模板中引用新增的属性

现在在 Category 和 Tag 列表中每一项都新增了一个 num_posts 属性记录该 Category 下的文章数量,我们就可以在模板中引用这个属性来显示分类下的文章数量了。

templates/blog/inclusions/_categories.html

<div class="widget widget-category">
  <h3 class="widget-title">分类</h3>
  <ul>
    {% for category in category_list %}
      <li>
        <a href="{% url 'blog:category' category.pk %}">{{ category.name }} <span class="post-count">({{ category.num_posts }})</span></a>
      </li>
    {% empty %}
      暂无分类!
    {% endfor %}
  </ul>
</div>

标签也是一样:

templates/blog/inclusions/_tags.html

<div class="widget widget-tag-cloud">
  <h3 class="widget-title">标签云</h3>
  <ul>
    {% for tag in tag_list %}
      <li>
        <a href="{% url 'blog:tag' tag.pk %}">{{ tag.name }} <span class="post-count">({{ tag.num_posts }})</a>
      </li>
    {% empty %}
      暂无标签!
    {% endfor %}
  </ul>
</div>

也就是在模板中通过模板变量 {{ category.num_posts }} 显示 num_posts 的值。开启开发服务器,可以看到分类名后正确地显示了该分类下的文章数了,而没有文章分类则不会在分类列表中出现。



『讲解开源项目系列』——让对开源项目感兴趣的人不再畏惧、让开源项目的发起者不再孤单。跟着我们的文章,你会发现编程的乐趣、使用和发现参与开源项目如此简单。欢迎留言联系我们、加入我们,让更多人爱上开源、贡献开源~

原文地址:https://www.cnblogs.com/xueweihan/p/12103739.html

时间: 2024-10-16 13:05:41

统计各个分类和标签下的文章数的相关文章

wordpress统计某个标签下的文章总数

在某个页面,我们需要在其后面显示此标签下共有多少文章,做个统计,可以用下面的两个函数来调用. //根据标签ID获取文章数 function get_tag_post_count_by_id( $tag_id ) { $tag = get_term_by( 'id', $tag_id, 'post_tag' ); _make_cat_compat( $tag ); return $tag->count; } //根据标签别名获取文章数 function get_tag_post_count_by_

让wordpress标签云显示文章数的正确方法

先看一下效果 在百度经验找到一个教程,可惜,根据实践发现方法是错误的, 百度经验上的代码: 1 2 3 4 5 6 7 8 9 10 11 12 //标签tag所包含的文章数量 function Tagno($text) { $text = preg_replace_callback('|<a>|i', 'tagnoCallback', $text); return $text; } function tagnoCallback($matches) { $text=$matches[1]; p

查询个人站点的文章、分类和标签查询

urls.py re_path('^(?P<username>\w+)$', views.home_site, name='home_site'), home_site.py def home_site(request, username): """ 个人站点视图函数 :param request: :return: """ user = UserInfo.objects.filter(username=username).first

新闻资讯类系统 ---- 文章分类和标签的设计

零. 简介 博客. CMS(网易新闻. 腾讯新闻) 之类的系统, 核心就是文章, 一切的一切都围绕着文章进行, 所以设计一个好的文章分类和标签的数据库关系模型, 对后续编码及维护将会起到至关重要的作用. 一. 分类 一篇文章, 比如 <大陆 ** 明星又离婚了> 这属于 「娱乐」 类新闻, 又属于 「中国」 分类下的新闻, 所以文章和分类的关系一般是 1 对 N . 数据库表结构设计 article : 字段名 注释 id title 文章标题 author 作者 create_time 创建

文章分类和标签的数据库设计

几乎在所有web项目中,都涉及文章分类和标签的设计,应该说这是一个比较常见.典型的案例.站长并不保证我的思路就是最好的,只是分享出来大家一起交流一下,互相促进与提高. 我们假设的开发项目是一个博客系统,最核心的部分就是与文章相关的,那么我们今天讨论如何设计博客系统的文章分类和标签. 1.首先,分类和标签都是要和具体的文章相关联的,当然也可能一些文章既没有分类也没有标签,这一点是大家在写查询的时候容易疏忽的地方.因为我们的第一感觉就是,在查询文章列表的时候关联分类表,查出所有的文章和分类,对应关系

wordpress搜索某分类下的文章

<!-- 搜索某分类下的文章 value="5" 为搜索id值为5的分类--> <form action="<?php bloginfo('home'); ?>" method="get"> <div> <input type="text" value="<?php echo wp_specialchars($s, 1); ?>" name=

ThinkCMF(二):多个分类下的文章显示并分页;

一.查找多个分类下的文章放在一个页面显示并分类where:id in(1,2,3); <php> $posts=sp_sql_posts_paged('field:post_title,post_date,object_id,term_id;order:post_date desc;where:term_id in(1,2,3)'); </php> <foreach name="posts['posts']" item="v">

wordpress在首页列出所有分类及分类下的文章

前面我们谈了wordpress调用指定分类文章,如果想要调用所有分类的文章如何实现呢?比如在wordpress首页列出所有分类及分类下的文章,其实方法类似 <?php $cats = get_categories(); foreach ( $cats as $cat ) { query_posts( 'showposts=10&cat=' . $cat->cat_ID ); ?> <h3><?php echo $cat->cat_name; ?>&l

Hexo next 主题配置右侧栏的分类和标签打开的是空白

基本搭建好后发现,分类或标签点进去网页不存在,但是具体的一个分类能够进去. 原来是没有设置好,解决起来也很简单. 一. 新建 标签 和分类 页面 在根目录输入命令 hexo new page categories 会自动新建 categorier 文件夹并生成一个index.md文件,将里面的代码改为: --- title: categories date: 2020-01-21 12:12:26 type: "categories" --- 同理,「标签」也一样 hexo new p