2020/04/24/c10530424102709.png

实现分类文章统计的功能

当我们还没进入某个分类页面时,就可以在外面看到这个分类下的所有文章数量

思路

我们可以先获取所有分类

然后依次对每个分类进行文章统计

实现

文章分类

blog_cates = BlogCate.objects.all() # 获取所有的分类
blog_cate_lists = [] # 新建一个列表用于保存各个分类对象
for blog_cate in blog_cates:
    blog_cate.blog_count = Blog.objects.filter(class_name=blog_cate).count()
    blog_cate_lists.append(blog_cate)
    
# ...
context['blog_cates'] = blog_cate_lists

模板 blog_cate.blog_cate_count 这个就是分类的数量

{% for blog_cate in blog_cates %}
<li class="list-group-item">
    <span class="badge">{{ blog_cate.blog_cate_count }}</span>
    <a href="{% url 'blog_list' blog_cate.pk %}">{{ blog_cate.class_name }}</a>
</li>
{#                                <li><a href="{% url 'blog_list' blog_cate.pk %}">{{ blog_cate.class_name }}</a></li>#}
{% empty %}
<li>当前暂无分类</li>
{% endfor %}

上面这个方法思路非常的清晰,就是写起来有点点麻烦

from django.db.models import Count
blog_cates = BlogCate.objects.annotate(blog_count=Count('blog'))

上面这个写法,blog_cates里面就有一个blog_count

blog_count就记录对应分类的统计

如果学过mysql,可以看下方的这个查询语句

SELECT blog_blogcate.id, blog_blogcate.class_name, COUNT(blog_blog.id) AS blog_count

FROM blog_blogcate LEFT OUTER JOIN blog_blog ON (blog_blogcate.id = blog_blog.class_name_id) 

GROUP BY blog_blogcate.id, blog_blogcate.class_name

GROUP BY进行分类操作

分类之后,COUNT(blog_blog.id)对每个分类进行统计计数

这个思路是不是和我们上面说的一模一样。

blog_cates = BlogCate.objects.annotate(blog_count=Count('blog'))

再来解释这句语句

blog_count : 获取到数据之后保存的别名
Count('blog'): 对模型中的blog进行计数,模型的类名是`Blog`  这里填小写即可

还有一个annotate方法,这个功能是为QuerySet中的每一项生成聚合¶

一头雾水,不知道说的啥,我用自己的话梳理一遍

BlogCate.objects.all()获取到的是一个QuerySet,里面有很多项,我们也知道可以拿来遍历输出

对于当中的每一项,可以使用annotate生成聚合或额外的数据

再来看下annotate的代码注释,翻译如下:

返回一个查询集,其中返回的对象包含额外的数据或聚合。

额外的数据?不就是我们的blog_count

如果学习过sql这个聚合还是蛮好理解的

这个是官方文档

https://docs.djangoproject.com/zh-hans/3.0/topics/db/aggregation/#generating-aggregates-for-each-item-in-a-queryset

我感觉还是解释的不太清楚,再来举个例子

2020/04/24/d4a770424113047.png

这次我们用一个单表来举例,上面那个是多表查询

Blog.objects.all()我们知道这个可以查询出所有的文章

现在对每一篇文章进行max(id),每篇文章的id只有一个,最大的id就是自己的id,这个没有什么实际意义,就是举个例子

annotate返回一个生成的聚合或者额外的数据

我使用p[0].id_max的输出了,最大id,就是他自身的id

这个id_max是Django为我们生成的名字,我们可以自己修改,就像下方这样

2020/04/24/cc19a0424113258.png

我们用m接收,p[0].m就可以输出了,m就是一个自己设置的别名

文章归档

除了文章分类以外,还有个文章归档

分类是有一个单独的表,所以很快的可以直接取得所有分类

对于文章归档是一样的,我们需要获取所有文章归档分类

blog_dates = Blog.objects.dates('created_time', 'month', 'DESC') # 获取按月分类的所有归档

现在要知道,每个文章归档下面的文章数量

# 文章归档和文章分类不一样,文章归档在上方拿到的是一个查询集,并不像上方文章分类一样是一个类的对象(对象可以直接添加新的变量)
blog_date_dict = {}
for blog_date in blog_dates:
    # 根据年月条件筛选
    blog_count = Blog.obejcts.filter(created_time__year=blog_date.year, created_time__month=blog_date.month).count()
    blog_date_dict[blog_date] = blog_count

# ...
context['blog_dates'] = blog_date_dict

模板实现,因为blog_date_dict是一个字典,所以遍历的时候

去遍历blog_datesitems 可以同时取出keyvalue

<ul class="list-group">
    {% for blog_date, blog_count in blog_dates.items %}
        <li class="list-group-item">
            <span class="badge">{{ blog_count }}</span>
            <a href="{% url 'blog_archive' blog_date.year blog_date.month %}">{{ blog_date|date:"Y年m月" }}</a>
        </li>
    {% endfor %}
</ul>

最后实现的效果

2020/04/24/5360d0424105905.png

Last modification:April 24th, 2020 at 04:09 pm