背景:

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

为查询集生产聚合:

Django 提供两种方式生成聚合。第一种方式是对一整个 QuerySet 进行汇总。例如,你想知道上架书籍的平均价格。Django 的查询语法提供了一种得到所有图书的方法:

Book.objects.all()

在此基础上,我们需要有一个方法来对属于 QuerySet 的对象值进行计算汇总。只要在 QuerySet 上使用 aggregate() 从句即可:

from django.db.models import Avg
Book.objects.all().aggregate(Avg('price'))
>>{'price__avg': 34.35}

例中的 all() 可以省略,所以能简化为:

Book.objects.aggregate(Avg('price'))
>>{'price__avg': 34.35}

aggregate() 从句的参数反映了我们想计算的聚合值,在这个例子就是,我们要算的就是 Book model 中 price 字段的平均值。可用的聚合函式在 查询集参考(QuerySet reference)中有详细的列表介绍。 对 QuerySet 而言,aggregate() 是一个结尾从句(意味着使用它后不能再使用其它queryset方法),它会返回一个键值对字典,其中键名是聚合的标识,键值是计算得出的结果,键名是根据聚合字段的名称和聚合函式的名称得到的。 如果你想手动指定聚合名称,你可以在指定聚合从句时提供聚合名称:

Book.objects.aggregate(average_price=Avg('price'))
>>{'average_price': 34.35}

如果你想生成不止一个聚合,你只要在 aggregate() 从句中添加另一个聚合即可,所以,如果我们还想知道所有书籍的最高价和最低价,可以这样写:

from django.db.models import Avg, Max, Min, Count
Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
>>{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

对查询集中的每一项都生成聚合

第一种方式是为查询集的所有项求聚合,第二种就是对查询集合的每一项都做聚合,比如,你想知道每本书有多少个作者,书与作者之间是多对多关系,我们得出 QuerySet 中每本书有多少个关联即可。 1.对每个对象生成汇总是通过 annotate() 从句实现的,在指定 annotate() 从句时, QuerySet 中的每个对象都根据所指定的值得到注解,注解语法与使用 aggregate() 从句的语法是相同的,annotate() 中的每个参数都代表一个要计算的聚合。例如,将作者的数量作为图书的注解:

# Build an annotated queryset
>>> q = Book.objects.annotate(Count('authors'))
# Interrogate the first object in the queryset
>>> q[0]
<Book: The Definitive Guide to Django>
>>> q[0].authors__count
2
# Interrogate the second object in the queryset
>>> q[1]
<Book: Practical Django Projects>
>>> q[1].authors__count
1

2.和使用 aggregate() 一样,注解的名称也根据聚合函式的名称和聚合字段的名称得到的。你可以在指定注解时,为默认名称提供一个别名:

>>> q = Book.objects.annotate(num_authors=Count('authors'))
>>> q[0].num_authors
2
>>> q[1].num_authors
1

与 aggregate() 不同的是, annotate() 不是一个结尾从句。 annotate() 从句的返回结果是一个查询集 QuerySet,这个查询集 QuerySet 可以用任何 QuerySet 方法进行修改,包括 filter(), order_by, 甚至是再次应用 annotate()。

3.annotate()同时支持关联数据的聚合,我们可以用双下划线去表示关联关系,django会得到关联数据的聚合。 例如,要得到每个书店的价格区别,可以如下这般使用注解:

>>> Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))

这段代码告诉 Django 获取书店(Store) model,关联(通过多对多关系)的图书(Book) model,然后对每本书的价格进行聚合,得出最小值和最大值。 同样的规则也用于 aggregate() 从句。如果你想知道所有书店中最便宜的书和最贵的书价格分别是多少:

>>> Store.objects.aggregate(min_price=Min('books__price'), max_price=Max('books__price'))

关系链可以按你的要求一直延伸。例如,想得到所有作者当中最小的年龄是多少,就可以这样写:

>>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))

聚合和其他查询集从句

1.filter() and exclude():

聚合也可以在过滤器中使用。作用于普通 model 字段的任何 filter() (或 exclude()) 都会对聚合涉及的对象进行限制。 使用 annotate() 从句时,过滤器有限制注解对象的作用。例如,你想得到以 “Django” 为书名开头的图书作者的总数:

>>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))

使用 aggregate() 从句时,过滤器有限制聚合对象的作用。例如,你可以算出所有以 “Django” 为书名开头的图书平均价格:

>>> Book.objects.filter(name__startswith="Django").aggregate(Avg('price'))

2.对注解过滤(Filtering on annotations):

注解值也可以被过滤。象使用其他 model 字段一样,注解也可以在 filter() 和 exclude() 从句中使用别名。 例如,要得到不止一个作者的图书,可以用:

>>> Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)

这个查询先生成一个注解结果,然后再生成一个作用于注解上的过滤器。 3.annotate() 和 filter() 从句的顺序:

编写一个包含 annotate() 和 filter() 从句的复杂查询时,要特别注意作用于 QuerySet 的从句的顺序。 当一个 annotate() 从句作用于某个查询时,要根据查询的状态才能得出注解值,而状态由 annotate() 位置所决定。所以这就导致 filter() 和 annotate() 不能交换顺序,下面两个查询就是不同的:

>>> Publisher.objects.annotate(num_books=Count('book')).filter(book__rating__gt=3.0)

另一个查询:

>>> Publisher.objects.filter(book__rating__gt=3.0).annotate(num_books=Count('book'))

两个查询都返回了至少出版了一本好书(评分大于 3 分)的出版商。但是第一个查询的注解包含其该出版商发行的所有图书的总数;而第二个查询的注解只包含出版过好书的出版商的所发行的图书总数。在第一个查询中,注解在过滤器之前,所以过滤器对注解没有影响。在第二个查询中,过滤器在注解之前,所以,在计算注解值时,过滤器就限制了参与运算的对象的范围。 4.order_by():

注解可以用来做为排序项。在你定义 order_by() 从句时,你提供的聚合可以引用定义的任何别名做为查询中 annotate() 从句的一部分。 例如,根据一本图书作者数量的多少对查询集 QuerySet 进行排序:

>>> Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')

5.values():

通常,注解是添加到每一个对象上的,一个执行了注解操作的查询集 QuerySet 所返回的结果中,每个对象都添加了一个注解值。但是,如果使用了 values() 从句,它就会限制结果中列的范围,对注解赋值的方法就会完全不同。就不是在原始的 QuerySet 返回结果中对每个对象中添加注解,而是根据定义在 values() 从句中的字段组合对先结果进行唯一的分组(放在vaues中的fields对应的各个值若相同则会归为一组),再根据每个分组算出注解值,这个注解值是根据分组中所有的成员计算而得的:

>>> Author.objects.annotate(average_rating=Avg('book__rating'))

这段代码返回的是数据库中所有的作者以及他们所著图书的平均评分。 但如果你使用了 values() 从句,结果就就会截然不同:

>>> Author.objects.values('name').annotate(average_rating=Avg('book__rating'))

在这个例子中,作者会按名称分组,所以你只能得到某个唯一的作者分组的注解值。这意味着如果你有两个作者同名,那么他们原本各自的查询结果将被合并到同一个结果中;两个作者的所有评分都将被计算为一个平均分。 6.annotate() 和 values() 从句的顺序:

和使用 filter() 从句一样,作用于某个查询的 annotate() 和 values() 从句的使用顺序是非常重要的。如果 values() 从句在 annotate() 之前,就会根据 values() 从句产生的分组来计算注解。 但是,如果 annotate() 从句在 values() 从句之前,就会根据整个查询集生成注解。在这种情况下,values() 从句只能限制输出的字段范围。 举个例子,如果我们互换了上个例子中 values() 和 annotate() 从句的顺序:

>>> Author.objects.annotate(average_rating=Avg('book__rating')).values('name', 'average_rating')

这段代码将给每个作者添加一个唯一的字段,但只有作者名称和 average_rating 注解会返回在输出结果中。 你也应该注意到 average_rating 显式地包含在返回的列表当中。之所以这么做的原因正是因为 values() 和 annotate() 从句。 如果 values() 从句在 annotate() 从句之前,注解会被自动添加到结果集中;但是,如果 values() 从句作用于 annotate() 从句之后,你需要显式地包含聚合列。

7.与默认排序或order_by()交互(Interaction with default ordering or order_by()):

在查询集中的 order_by() 部分(或是在 model 中默认定义的排序项) 会在选择输出数据时被用到,即使这些字段没有在 values() 调用中被指定。这些额外的字段可以将相似的数据行分在一起,也可以让相同的数据行相分离。在做计数时,就会表现地格外明显: 经由这个例子来认识一下,假设你有下面这样一个 model:

class Item(models.Model):
name = models.CharField(max_length=10)
data = models.IntegerField()
class Meta:
ordering = ["name"]

这关键的部分就是在 model 默认排序项中设置的 name 字段。如果你想知道每个非重复的 data 值出现的次数,可以这样写:

# Warning: not quite correct!
Item.objects.values("data").annotate(Count("id"))

…这部分代码想通过使用它们公共的 data 值来分组 Item 对象,然后在每个分组中得到 id 值的总数。但是上面那样做是行不通的。这是因为默认排序项中的 name 也是一个分组项,所以这个查询会根据非重复的 (data, name) 进行分组,而这并不是你本来想要的结果。所以,你应该这样改写:

Item.objects.values("data").annotate(Count("id")).order_by()

…这样就清空了查询中的所有排序项。你也可以在其中使用 data ,这样并不会有副作用,这是因为查询分组中只有这么一个角色了。 这个行为与查询集文档中提到的 distinct() 一样,而且生成规则也一样:一般情况下,你不想在结果中由额外的字段扮演这个角色,那就清空排序项,或是至少保证它仅能访问 values() 中的字段。 注意 你可能想知道为什么 Django 不删除与你无关的列?主要原因就是要保证使用 distinct() 和其他方法的一致性。Django 永远不会 删除你所指定的排序限制(我们不能改动那些方法的行为,因为这会违背 API 稳定性 原则)。

聚合注解

你也可以在注解的结果上生成聚合。当你定义一个 aggregate() 从句时,你提供的聚合会引用定义的任何别名做为查询中 annotate() 从句的一部分。 例如,如果你想计算每本书平均有几个作者,你先用作者总数注解图书集,然后再聚合作者总数,引入注解字段:

>>> Book.objects.annotate(num_authors=Count('authors')).aggregate(Avg('num_authors'))
{'num_authors__avg': 1.66}

Django聚合数据的更多相关文章

  1. 聚合数据董铭彦:小程序开发的兴起将带火API数据交易

    2016中关村大数据日活动近日在京举办,今年新进驻北京的聚合数据受邀参与,在13日举行的大数据交易专场论坛上,聚合数据副总裁董铭彦与参会嘉宾以"共筑数据交易产业生态,共享大数据时代红利&qu ...

  2. 聚合数据全国天气预报api接口

    查询天气预报在APP中常用的一个常用功能,聚合数据全国天气预报api接口可以根据根据城市名/id查询天气.根据IP查询天气.据GPS坐标查询天气.查询城市天气三小时预报,并且支持全国不同城市天气预报查 ...

  3. 聚合数据天气预报API-ajax 通过城市名取数据

    如需要,可申请聚合数据天气预报API:https://www.juhe.cn/docs/api/id/39,并生成AppKey. 接口地址:http://v.juhe.cn/weather/index ...

  4. Django Model数据访问Making queries

    创建完Model之后, Django 自动为你提供一套数据库抽象层的API,利用它可以完成创建,提取,更新,删除对象的操作. 以下面的Model为例: class Blog(models.Model) ...

  5. 功能:使用QQ号登陆,并加上微信和短信提醒,是否增量备份可选,阿里大鱼短信发送开发与测试,聚合数据(用JSON发短信,比较清楚)

    微博就可以,所以其它软件也可以http://desktop.weibo.com/ http://blog.csdn.net/jueblog/article/details/14497181http:/ ...

  6. Android通过聚合数据API实现天气预报

    使用聚合数据的API 聚合数据地址:https://www.juhe.cn/ 在数据服务->生活常用->全国天气预报,申请天气预报的API使用的KEY 保存请求示例的地址,把您申请的KEY ...

  7. Django聚合与分组查询中value与annotate的顺序问题

    在学习Django聚合与分组查询中,发现value与annotate的顺序不同时,查询结果大相径庭,经过一下午的研究,终于弄明白了,现在分享给大家,先上结论: 结论 value在annotate前面时 ...

  8. django model数据 时间格式

    from datetime import datetime dt = datetime.now() print '时间:(%Y-%m-%d %H:%M:%S %f): ' , dt.strftime( ...

  9. PHP 获取IP地址位置信息「聚合数据API」

    聚合数据 提供了[查询IP所属区域]的服务接口,只需要以 GET 请求的方式向 API 传入 IP地址 和 APPKEY 即可获得查询结果. 这里的难点主要在于如何通过PHP获取客户端IP地址,以及如 ...

随机推荐

  1. 被弃用的php函数以及用来替代的函数

    下面列举了部分被弃用的函数: call_user_method()(使用 call_user_func() 替代) call_user_method_array() (使用 call_user_fun ...

  2. 单调队列优化DP || [NOI2005]瑰丽华尔兹 || BZOJ 1499 || Luogu P2254

    题外话:题目极好,做题体验极差 题面:[NOI2005]瑰丽华尔兹 题解: F[t][i][j]表示第t时刻钢琴位于(i,j)时的最大路程F[t][i][j]=max(F[t-1][i][j],F[t ...

  3. json与string与map的理解

    json是一种特殊格式的string字符串,也就是json也是string类型,只是这种string是有格式的,那么他的格式就是类似map的格式[key:value] 举例子: Map map = r ...

  4. CPU、CPU核与线程的关系

    CPU相关概念: CPU:独立的中央处理单元,体现在主板上是有多个CPU的插槽. CPU cores:在每一个CPU上,都可能有多个核(core),每一个核中都有独立的一套ALU.FPU.Cache等 ...

  5. Vue框架使用sass

    引入: cnpm install node-sass --save-dev //安装node-sass cnpm install sass-loader@7.3.1 --save-dev cnpm i ...

  6. 补充:bytes类型以及字符编码转换

    内容转自小猿圈链接:https://book.apeland.cn/details/41/ 定义 bytes类型是指一堆字节的集合,在python中以b开头的字符串都是bytes类型 b'\xe5\x ...

  7. PIXI如何绘制离屏canvas到舞台上

    有个方法是toDataURL(),原生的,先转换成图片再绘制. 但是pixi提供了一个BaseTexture,其构造函数的参数可以是一个canvas 因此可以直接使用如下代码绘制canvas //微信 ...

  8. Winserver-禁止程序启动

    注册表限制程序启动 经测试,可以阻止手动启动,但在job中还是会有启动的进程,这个待确定. run→regedit 添加程序只写exe名就行 手动不能运行了

  9. HTML5测试(一)

    HTML5测试一 1. 问题:HTML5 之前的 HTML 版本是什么? A.HTML 4.01 B.HTML 4 C.HTML 4.1 D.HTML 4.9 答案:A HTML5 是 HTML 最新 ...

  10. Linux 环境下 gzip 的加解密命令

    1.加密 [root@127-0-0-1 nginx]# gzip -v access.log-20190328 access.log-20190328: 95.8% -- replaced with ...