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. linux sftp 和scp 运用

    Linux scp命令: Upload  to remote :复制本地文件到远程 Part1: Scp  -P port  local_file  remote_user@remote_ip:rem ...

  2. mvc 部分页

    在一些复杂的项目中,我们经常会遇到一个页面存在很多模块,存在页面交互或者加载数据过多等问题,这种时候,我们很可能会考虑到通过使用部分页来解决这个问题(ps:当然还有很多的解决方案,在这里只是简单介绍这 ...

  3. Python整合pdf【新手必学】

    在下载课件时往往会分成很多个小的pdf,一个也就几页,想要整合成一整个大pdf,于是百度了一下,网上有很多在线的pdf整合器,但是由于这蛋疼的网速,流量还要花钱,还是想要本地搞. 说python是万能 ...

  4. 6_8 树(UVa548)<从中序和后序恢复二叉树>

    你的任务是找出一棵二叉树中最小路径上终端节点(树叶,leaf node)的值.所谓路径乃指从根节点(root)旅行到任一终端节点.路径的值为所经过的节点的值的和(包含根节点及终端节点).而最小路径就是 ...

  5. bootstrap当中,实现div居中

    文本内容居中:利用bootstrap自带CSS样式表当中 的   text-center 样式来实现 <div class="row form-group text-center&qu ...

  6. HDU 2063 过山车(二分图 && 匈牙利 && 最小点覆盖)

    嗯... 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2063 这是一道很经典的匈牙利问题: 把男同学看成左边点,女同学看成右边点,如果两个同学愿意同 ...

  7. CSS实现心形、六角星、六边形、平行四边形等几何

    本文将利用border属性实现简单几何的绘制: 效果图: 正八角星 说明:采用两个正方形以中心进行旋转叠加: /* 八角星 */ #burst-8 { background: #6376ff1f; w ...

  8. bitnami-redmine 一键安装

    下载bitnami-redmine https://bitnami.com/stack/redmine 安装 选择语言 设置登陆用户名和密码,数据库用户名root,数据库密码也是这个设置的密码 其他下 ...

  9. Python NumPy中数组array.min(0)返回数组

    如果没有参数min()返回一个标量,如果有参数0表示沿着列,1表示沿着行.

  10. pdf.js的使用 (3)真实项目分享

    需求:a.jsp页面要做一个pdf的预览功能,我采用layer.open()弹窗的形式来预览pdf 1.在a.jsp点击文件然后弹出窗口(其实是弹出b.jsp) var lay=layer.open( ...