Django数据库抽象API描述了使用Django queries来增删改查单个对象的方法。
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
class Publisher(models.Model):
name = models.CharField(max_length=300)
class Book(models.Model):
name = models.CharField(max_length=300)
pages = models.IntegerField()
price = models.DecimalField(max_digits=10, decimal_places=2)
rating = models.FloatField()
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
pubdate = models.DateField()
class Store(models.Model):
name = models.CharField(max_length=300)
books = models.ManyToManyField(Book)
速查表
# Total number of books.
>>> Book.objects.count()
2452
# Total number of books with publisher=BaloneyPress
>>> Book.objects.filter(publisher__name='BaloneyPress').count()
73
# Average price across all books.
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}
# Max price across all books.
>>> from django.db.models import Max
>>> Book.objects.all().aggregate(Max('price'))
{'price__max': Decimal('81.20')}
# Difference between the highest priced book and the average price of all books.
>>> from django.db.models import FloatField
>>> Book.objects.aggregate(
... price_diff=Max('price', output_field=FloatField()) - Avg('price'))
{'price_diff': 46.85}
# All the following queries involve traversing the Book<->Publisher
# foreign key relationship backwards.
# Each publisher, each with a count of books as a "num_books" attribute.
>>> from django.db.models import Count
>>> pubs = Publisher.objects.annotate(num_books=Count('book'))
>>> pubs
<QuerySet [<Publisher: BaloneyPress>, <Publisher: SalamiPress>, ...]>
>>> pubs[0].num_books
73
# Each publisher, with a separate count of books with a rating above and below 5
>>> from django.db.models import Q
>>> above_5 = Count('book', filter=Q(book__rating__gt=5))
>>> below_5 = Count('book', filter=Q(book__rating__lte=5))
>>> pubs = Publisher.objects.annotate(below_5=below_5).annotate(above_5=above_5)
>>> pubs[0].above_5
23
>>> pubs[0].below_5
12
# The top 5 publishers, in order by number of books.
>>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5]
>>> pubs[0].num_books
1323
在QuerySet上生成聚合
Django提供了两种生成聚合的方法。
第一种是从整个QuerySet生成汇总值。例想要计算所有在售书的平均价格
查询所有图书集合的语句
>>> Book.objects.all()
在QuerySet后添加aggregate()子句来计算QuerySet对象的汇总值。
>>> 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()是QuerySet的一个终端子句,使用后将返回“名称-值”的字典,其中名称就是聚合值的标志,值就是计算出的聚合结果。
名称是根据字段名和聚合函数而自动生成。如果像指定名称可以通过使用子句时指定
>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}
如果想生成更多的聚合内容,只需在aggregate()子句中加入其他参数即可。
示例
- 加上书中最高和最低的价格
>>> from django.db.models import Avg, Max, Min
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
为QuerySet中的每一个条目生成聚合
生成值的汇总的另一个办法是为QuerySet的每一个对象生成独立汇总。
例:如果想检索书籍列表, 并查询每本书有多少个作者。每一本书与作者有多对多的关系,想在QuerySet中为每一本书总结这个关系。
使用annotate()子句可以生成每一个对象的汇总。
示例
- 注解(annotate)所有书的所有作者
# Build an annotated queryset
>>> from django.db.models import Count
>>> 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
同样可以在注解时自定字段名
>>> q = Book.objects.annotate(num_authors=Count('authors'))
>>> q[0].num_authors
2
>>> q[1].num_authors
1
注:annotate()不是终端子句。输出的是QuerySet。
组合多个聚合
使用annotate()组合多个聚合将产生错误的结果,因为它使用连接而不是子查询
>>> book = Book.objects.first()
>>> book.authors.count()
2
>>> book.store_set.count()
3
>>> q = Book.objects.annotate(Count('authors'), Count('store'))
>>> q[0].authors__count
6
>>> q[0].store__count
6
Count聚合可以使用distinct参数避免错误结果
>>> q = Book.objects.annotate(Count('authors', distinct=True), Count('store', distinct=True))
>>> q[0].authors__count
2
>>> q[0].store__count
3
连接和聚合
在聚合函数里面指定聚合的字段时,Django允许在过滤相关字段的时候使用相同的双下划线表示法。Django将处理任何需要检索和聚合的关联值得表连接。
示例
- 想要寻找每个书店提供的书籍价格区间
>>> from django.db.models import Max, Min
>>> Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))
过程是让Django去检索Store模型,连接(通过多对多关系)Book模型,并且聚合书籍模型的价格字段来获取最大最小值。
使用aggregate()从句
>>> Store.objects.aggregate(min_price=Min('books__price'), max_price=Max('books__price'))
Join链可以根据需求尽可能深的查询
示例
- 提取所出售的书籍中最年轻的作者年龄
>>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))
反向关系
类似于跨关系查询
示例
- 要求所有出版社,注解他们各自的图书库存总数
>>> from django.db.models import Avg, Count, Min, Sum
>>> Publisher.objects.annotate(Count('book'))
查询结果里的每一个Publisher会有多余的属性(book__count)
- 找出最古老的一本
>>> Publisher.objects.aggregate(oldest_pubdate=Min('book__pubdate'))