https://www.zmrenwu.com/post/18/

博客文章通常都有分类,有时候我们会看到分类名后面还跟着该分类下的文章数量。前面我们通过学习 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 下的其它操作数据库的方法,以便在遇到相关问题时知道去哪里查阅。

django annotate()的使用的更多相关文章

  1. Django Aggregation聚合

    在当今根据需求而不断调整而成的应用程序中,通常不仅需要能依常规的字段,如字母顺序或创建日期,来对项目进行排序,还需要按其他某种动态数据对项目进行排序.Djngo聚合就能满足这些要求. 以下面的Mode ...

  2. Django 1.10中文文档-聚合

    Django 数据库抽象API 描述了使用Django 查询来增删查改单个对象的方法. 然而,有时候你要获取的值需要根据一组对象聚合后才能得到. 这个主题指南描述了如何使用Django的查询来生成和返 ...

  3. django聚合查询

    聚合¶ Django 数据库抽象API 描述了使用Django 查询来增删查改单个对象的方法.然而,有时候你需要获取的值需要根据一组对象聚合后才能得到.这份指南描述通过Django 查询来生成和返回聚 ...

  4. Django 聚合与查询集API实现侧边栏

    本文从Django官方文档总结而来,将聚合的主要用法和查询集的常见方法做一归纳. 聚合 1. 聚合的产生来源于django数据库查询,通常我们使用django查询来完成增删查改,但是有时候需要更复杂的 ...

  5. Django Aggregation聚合 django orm 求平均、去重、总和等常用方法

    Django Aggregation聚合 在当今根据需求而不断调整而成的应用程序中,通常不仅需要能依常规的字段,如字母顺序或创建日期,来对项目进行排序,还需要按其他某种动态数据对项目进行排序.Djng ...

  6. Django学习笔记之Django ORM Aggregation聚合详解

    在当今根据需求而不断调整而成的应用程序中,通常不仅需要能依常规的字段,如字母顺序或创建日期,来对项目进行排序,还需要按其他某种动态数据对项目进行排序.Djngo聚合就能满足这些要求. 以下面的Mode ...

  7. 笔记15:Django提升篇

    django提升 (1)打包应用程序:可重用性 打包 Python 程序需要工具:setuptools .打包时候建议使用django-appname 1 在你的 Django 项目目录外创建一个名为 ...

  8. Django文档阅读之聚合

    聚合 我们将引用以下模型.这些模型用来记录多个网上书店的库存. from django.db import models class Author(models.Model): name = mode ...

  9. Django聚合数据

    背景: 有些时候,光靠数据库中已有字段的数据,还不足以满足一些特殊场景的需求,例如显示一个作者的所有书籍数量. 这时候就需要在已有数据基础上,聚合出这些没有的数据. 为查询集生产聚合: Django ...

随机推荐

  1. yii components文件到底应该放些什么代码

    项目全局用的代码,比如项目所有controller和model的共通操作或者放一些第三方的组件.插件之类的项目全局用的代码

  2. 「JSOI2015」symmetry

    「JSOI2015」symmetry 传送门 我们先考虑构造出原正方形经过 \(4\) 种轴对称变换以及 \(2\) 种旋转变换之后的正方形都构造出来,然后对所得的 \(7\) 个正方形都跑一遍二维哈 ...

  3. Linux-VMware 15 虚拟机黑屏问题

    VMware 15 虚拟机黑屏问题   最近终于舍弃win7,换了win10的操作系统...   VM12不兼容,各种问题频出,于是换了VM15. 新装了kali2019.03,结果刚装好不久,在某一 ...

  4. Bugku-CTF加密篇之凯撒部长的奖励(就在8月,超师傅出色地完成了上级的特遣任务,凯撒部长准备给超师傅一份特殊的奖励.......)

    凯撒部长的奖励 就在8月,超师傅出色地完成了上级的特遣任务,凯撒部长准备给超师傅一份特殊的奖励,兴高采烈的超师傅却只收到一长串莫名的密文,超师傅看到英语字串便满脸黑线,帮他拿到这份价值不菲的奖励吧. ...

  5. Python入门,基本数据类型

    1.Python中的注释 单行注释:#注释内容 多行注释:三引号(单或者是双) ''' 注释内容 ''' """ 注释内容 """ 2.输入 ...

  6. C语言:将ss所指字符串中所有下标为奇数位上的字母转换成大写,若不是字母,则不转换。-删除指针p所指字符串中的所有空白字符(包括制表符,回车符,换行符)-在带头结点的单向链表中,查找数据域中值为ch的结点,找到后通过函数值返回该结点在链表中所处的顺序号,

    //将ss所指字符串中所有下标为奇数位上的字母转换成大写,若不是字母,则不转换. #include <stdio.h> #include <string.h> void fun ...

  7. C语言:假定输入的字符串只包含字母和*号,fun函数:除了尾部的*号以外,将字符的其他*号进行全部删除,形参p已经指向字符串中最后一个字母。-利用折半查找整数m在有序数组中的位置,若找到,返回下标值,否则返回-1。

    //假定输入的字符串只包含字母和*号,fun函数:除了尾部的*号以外,将字符的其他*号进行全部删除,形参p已经指向字符串中最后一个字母. #include <stdio.h> void f ...

  8. windows ,linux永久和临时修改pip源

    临时修改(建议)pypi镜像源方法:如果有untrust 报错,可使用https开头的网站,或加上--trusted 例如: pip install pywin32 -i http://mirrors ...

  9. Win Oracle 监听文件配置参考

    Win lister.ora配置参考 # listener.ora Network Configuration File: C:\app\Administrator\product\\dbhome_1 ...

  10. Booth算法: 补码一位乘法公式推导与解析

    以下讲解内容出自<计算机组成原理(第三版)>(清华大学出版社) 大二学生一只,我的计组老师比较划水,不讲公式推导,所以最近自己研究了下Booth算法的公式推导,希望能让同样在研究Booth ...