支持聚合函数的方法:
提到聚合函数,首先我们要知道的就是这些聚合函数是不能在django中单独使用的,要想在django中使用这些聚合函数,就必须把这些聚合函数放到支持他们的方法内去执行。支持聚合函数的方法有两种,分别是aggregate和annotate,这两种方法执行的原生SQL以及结果都有很大的区别,下面我们以实例操作的方式一一介绍:
# 示例模型:
class
Author(models.Model):
"""作者模型"""
name
=
models.CharField(max_length
=
100
)
age
=
models.IntegerField()
email
=
models.EmailField()
class
Book(models.Model):
"""图书模型"""
name
=
models.CharField(max_length
=
100
)
author
=
models.ForeignKey(
'Author'
,on_delete
=
models.CASCADE)
price
=
models.FloatField()
class
BookOrder(models.Model):
"""图书订单模型"""
book
=
models.ForeignKey(
'Book'
,on_delete
=
models.CASCADE)
sailprice
=
models.FloatField()
create_time
=
models.DateTimeField(auto_now_add
=
True
)
from
django.db.models
import
Avg
from
django.db
import
connection
result
=
Author.objects.aggregate(avg_age
=
Avg(
'age'
))
print
(connection.queries)
# 打印执行时所有的查询语句
result
=
Book.objects.aggregate(
sum
=
Sum
(
'bookorder__price'
))
2、annotate:这个方法不但可以执行聚合函数,也可以传递F、Q对象为当前QuerySet生成一个新的属性。
这个方法一般聚合的是连表中的字段,会为当前QuerySet中的每个对象生成一个独立的摘要,为查询的模型增加一个新的属性,这个属性的值就是使用聚合函数所得到的值,在使用这个聚合函数的时候annotate会使用这个模型的主键进行group by进行分组(注意这里只有在使用聚合函数生成新字段的时候会进行group by,在使用F、Q表达式增添新字段时,并不会使用group by),然后在连表中根据分组的结果进行聚合,这一点正符合为QuerySet中每个对象增加一个独立摘要的事实。
使用这个方法执行聚合函数,得到的结果是一个QuerySet对象,结果依然能够调用filter()、order_by()甚至annotate()进行再次聚合,现在我想提取每一本书的平均销售的价格(注意销售价格在BookOrder表中):
from
django.db.models
import
Avg
from
django.db
import
connection
books
=
Book.objects.annotate(avg
=
Avg(
'bookorder__sailprice'
))
for
book
in
books:
print
(
'%s/%s'
%
(book.name,book.avg))
# 注意这里的avg属性就是annotate执行聚合函数得到的
print
(connection.queries)
聚合函数:
在Django中,聚合函数都是在django.db.models模块下的,具体的聚合函数有Avg、Count、Max、Min、Sum,现在我们一一介绍这些函数的作用:
1、Avg:计算平均值,使用于与数值相关的字段,如果使用aggregate方法来执行这个函数,那么会得到一个字典,默认情况下,字典的键为field__avg,值为执行这个聚合函数所得到的值,示例代码如下:
# 计算所有作者的平均年龄
result
=
Author.objects.aggregate(Avg(
'age'
))
print
(result)
# 结果为:{"age__avg": 23.8}
# 如果想要使用自定义的键,那么可以把aggregate中的未知参数变为关键字参数,该关键字就是得到的键,示例代码如下:
result
=
Author.objects.aggregate(avgAge
=
Avg(
'age'
))
print
(result)
# 结果为:{"avgAge": 23.8}
# 如果使用annotate方法执行这个函数,那么得到的结果就是一个QuerySet对象,只不过这个对象中的每一个都会添加一个属性,这个属性的名称其实和上面的键一样,可以使用默认也可以自定义,使用方法与在aggregate中键名的定义一样,这里就不再赘述:
books
=
Book.objects.annotate(avg
=
Avg(
'bookorder__sailprice'
))
for
book
in
books:
print
(
'%s/%s'
%
(book.name,book.avg))
# 注意这里的avg属性就是annotate执行聚合函数得到的
print
(connection.queries)
# 计算总共有多少个订单
result
=
BookOrder.objects.aggregate(total
=
Count(
'id'
,distanct
=
True
))
print
(result)
# 结果为:{"total": 18}
# 计算每本书的订单量
books
=
Book.objects.annotate(total
=
Count(
'bookorder__id'
))
for
book
in
books:
print
(
'%s/%s'
%
(book.name,book.total))
3、Max和Min:计算某个字段的最大值和最小值,用法与Avg一样
4、Sum:计算总和,用法与Avg一样
注:总结一下,其实可以简单的理解使用aggregate时,是对QuerySet整个对象的某个属性汇总聚合,不会使用分组。而使用annotate方法时,是为QuerySet中的每个对象生成一个独立的摘要,一定会使用分组,然后再聚合。