聚合查询(aggregate)
聚合查询函数是对一组值执行计算,并返回单个值。
有表结构如下:
id | title | price | publish_id | pub_date |
---|---|---|---|---|
3 | 菜鸟教程 | 200.00 | 1 | 2010-10-10 |
4 | 冲灵剑法 | 100.00 | 1 | 2004-04-04 |
detail.html如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ res }}
</body>
</html>
计算所有图书的平均价格:
from django.db.models import Avg,Max,Min,Count,Sum # 引入函数
...
def add_book(request):
res = models.Book.objects.aggregate(Avg("price"))
return render(request, "detail.html", {"res": res});
// 输出:{'price__avg': Decimal('150.000000')}
计算所有图书的数量、最贵价格和最便宜价格:
res=models.Book.objects.aggregate(c=Count("id"),max=Max("price"),min=Min("price"))
// 输出:{'c': 2, 'max': Decimal('200.00'), 'min': Decimal('100.00')}
小结:
- 聚合查询返回值的数据类型是字典。不能再使用 QuerySet 数据类型的一些 API 了。
- 聚合函数 aggregate() 是 QuerySet 的一个终止子句, 生成的一个汇总值,相当于 count()。日期数据类型(DateField)可以用 Max 和 Min。
- 返回的字典中:键的名称默认是(属性名称加上__聚合函数名),值是计算出来的聚合值。
如果要自定义返回字典的键的名称,可以起别名:
aggregate(别名 = 聚合函数名("属性名称"))
分组查询(annotate)
分组查询一般会用到聚合函数。
返回值:
- 分组后,用
values
取值,则返回值是 QuerySet 数据类型里面为一个个字典
; - 分组后,用
values_list
取值,则返回值是 QuerySet 数据类型里面为一个个元组
。
MySQL 中的 limit 相当于 ORM 中的 QuerySet 数据类型的切片。
annotate 里面放聚合函数。
- values 或者 values_list 放在 annotate 前面:values 或者 values_list 是声明以什么字段分组,annotate 执行分组。
- values 或者 values_list 放在annotate后面: annotate 表示直接以当前表的pk执行分组,values 或者 values_list 表示查询哪些字段, 并且要将 annotate 里的聚合函数起别名,在 values 或者 values_list 里写其别名。
统计每一个出版社的最便宜的书的价格:
res = models.Publish.objects.values("name").annotate(in_price = Min("book__price"))
// 输出:<QuerySet [{'name': '华山出版社', 'in_price': Decimal('100.00')}, {'name': '明教出版社', 'in_price': None}]>
统计每一本书的作者个数1:
res = models.Book.objects.annotate(c = Count("authors__name")).values("title","c")
// 输出:<QuerySet [{'title': '菜鸟教程', 'c': 2}, {'title': '冲灵剑法', 'c': 1}]>
统计每一本书的作者个数2:values_list
res = models.Book.objects.annotate(c = Count("authors__name")).values_list("title","c")
// 输出:<QuerySet [('菜鸟教程', 2), ('冲灵剑法', 1)]>
统计每一本以"菜"开头的书籍的作者个数:
models.Book.objects.filter(title__startswith="菜").annotate(c = Count("authors__name")).values("title","c")
// 输出:<QuerySet [{'title': '菜鸟教程', 'c': 2}]>
统计不止一个作者的图书名称:
res = models.Book.objects.annotate(c = Count("authors__name")).filter(c__gt=1).values("title","c")
// 输出:<QuerySet [{'title': '菜鸟教程', 'c': 2}]>
根据一本图书作者数量的多少对查询集 QuerySet 进行降序排序:
res = models.Book.objects.annotate(c = Count("authors__name")).order_by("-c").values("title","c")
// 输出:<QuerySet [{'title': '菜鸟教程', 'c': 2}, {'title': '冲灵剑法', 'c': 1}]>
查询各个作者出的书的总价格:
res = models.Author.objects.annotate(all = Sum("book__price")).values("name","all")
// 输出:<QuerySet [{'name': '令狐冲', 'all': Decimal('300.00')}, {'name': '任我行', 'all': None}, {'name': '任盈盈', 'all': Decimal('200.00')}]>
小结:
- 使用 values => 字典,values_list => 元祖。
- value(或 value_list)在前,以values声明的字段分组,在后以pk分组。
F() 查询
F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
F 动态获取对象字段的值,可以进行运算。
Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取余的操作。
修改操作(update)也可以使用 F() 函数。
查询工资大于年龄的人:
from django.db.models import F
...
res = models.Emp.objects.filter(salary__gt=F("age")).values("name","age")
...
将每一本书的价格提高100元:
res = models.Book.objects.update(price=F("price")+100)
Q() 查询
Q(条件判断)
Q 对象可以使用 & | ~ (与 或 非)操作符进行组合。
查询价格小于 150 或者名称以菜开头的书籍的名称和价格。
rom django.db.models import Q
...
res=models.Book.objects.filter(Q(price__lt=150)|Q(title__startswith="菜")).values("title","price")
// 输出:<QuerySet [{'title': '菜鸟教程', 'price': Decimal('200.00')}, {'title': '冲灵剑法', 'price': Decimal('100.00')}]>
查询以"菜"结尾或者不是 2010 年 10 月份的书籍:
res = models.Book.objects.filter(Q(title__endswith="菜") | ~Q(Q(pub_date__year=2010) & Q(pub_date__month=10)))
// 输出:<QuerySet [{'title': '冲灵剑法', 'price': Decimal('100.00')}]>
查询出版日期是 2004 或者 1999 年,并且书名中包含有"菜"的书籍。
Q 对象和关键字混合使用,Q 对象要在所有关键字的前面:
res = models.Book.objects.filter(Q(pub_date__year=2004) | Q(pub_date__year=1999), title__contains="菜")