首页 > 代码库 > Django Aggregation数据库聚合查询
Django Aggregation数据库聚合查询
在当今根据需求而不断调整而成的应用程序中,通常不仅需要能依常规的字段,如字母顺序或创建日期,来对项目进行排序,还需要按其他某种动态数据对项目进行排序。Djngo聚合就能满足这些要求。
以下面的Model为例
from django.db import modelsclass Author(models.Model): name = models.CharField(max_length=100) age = models.IntegerField()class Publisher(models.Model): name = models.CharField(max_length=300) num_awards = models.IntegerField()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) pubdate = models.DateField()class Store(models.Model): name = models.CharField(max_length=300) books = models.ManyToManyField(Book) registered_users = models.PositiveIntegerField()
快速了解
# books总数量.>>> Book.objects.count()2452# Total number of books with publisher=BaloneyPress>>> Book.objects.filter(publisher__name=‘BaloneyPress‘).count()73# books的平均price.>>> from django.db.models import Avg>>> Book.objects.all().aggregate(Avg(‘price‘)){‘price__avg‘: 34.35}# books的最大price.>>> from django.db.models import Max>>> Book.objects.all().aggregate(Max(‘price‘)){‘price__max‘: Decimal(‘81.20‘)}# All the following queries involve traversing the Book<->Publisher# many-to-many relationship backward# 为每个publisher添加个num_books属性,即每个pulisher出版的book的数量.>>> from django.db.models import Count>>> pubs = Publisher.objects.annotate(num_books=Count(‘book‘))>>> pubs[<Publisher BaloneyPress>, <Publisher SalamiPress>, ...]>>> pubs[0].num_books73# 根据num_book属性排序.>>> pubs = Publisher.objects.annotate(num_books=Count(‘book‘)).order_by(‘-num_books‘)[:5]>>> pubs[0].num_books1323
聚合生成Generating aggregates over a QuerySet
Django有两种方法来生成聚合。第一种方法是为整个QuerySet生成聚合值,例如为全部的books生成price的平均值:
>>> from django.db.models import Avg>>> Book.objects.all().aggregate(Avg(‘price‘)){‘price__avg‘: 34.35}
可以简略为:
>>> Book.objects.aggregate(Avg(‘price‘)){‘price__avg‘: 34.35}
函数aggregate()的参数是一系列聚合函数aggregate functions:
Avg
返回平均值
Count
class Count(field, distinct=False)
返回计数。当参数distinct=True时,返回unique的对象数目。
Max
返回最大值
Min
返回最小值.
StdDev
class StdDev(field, sample=False)
返回标准偏差
有一个参数sample
默认情况下sample=False,返回总体标准偏差,如果sample=True,返回样本标准偏差。
Sum
返回总值
Variance
class Variance(field, sample=False)
返回方差
有一个参数sample,默认返回总体方差,sample设为True时返回样本方差。
aggregate()方法被调用时,返回一个键值对字典,可以指定key的名字:
>>> Book.objects.aggregate(average_price=Avg(‘price‘)){‘average_price‘: 34.35}
如果你想生成多个聚合,你只需要添加另一个参数。所以,如果我们还想知道所有书的最高和最低的价格:
>>> 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‘)}
为查询集的每个对象生成聚合值Generating aggregates for each item in a QuerySet
这是生成聚合值的第二种方法。比如你要检索每本书有多少个作者。book和author是manytomany的关系,我们可以为每本书总结出这种关系。
每个对象的总结可以用方法annotate()生成:
# 建立一个annotate QuerySet>>> from django.db.models import Count>>> q = Book.objects.annotate(Count(‘authors‘))# 第一个对象>>> q[0]<Book: The Definitive Guide to Django>>>> q[0].authors__count2# 第二个对象>>> q[1]<Book: Practical Django Projects>>>> q[1].authors__count1
也可以指定生成属性的名字:
>>> q = Book.objects.annotate(num_authors=Count(‘authors‘))>>> q[0].num_authors2>>> q[1].num_authors1
和aggregate()不同,annotate()的输出是一个QuerySet。
联合聚合Joins and aggregates
目前为止,我们聚合查询的field都属于我们要查询的Model,我们也可以用其它Model的field来进行聚合查询,例如:
>>> from django.db.models import Max, Min>>> Store.objects.annotate(min_price=Min(‘books__price‘), max_price=Max(‘books__price‘))
这样就可以查询每个Store里面所有books的价格范围
联合链的深度可以随心所欲:
>>> Store.objects.aggregate(youngest_age=Min(‘books__authors__age‘))
反向关系Following relationships backwards
通过book反向查询publisher:
>>> from django.db.models import Count, Min, Sum, Avg>>> Publisher.objects.annotate(Count(‘book‘))
返回的QuerySet的每个publisher都会带一个属性book_count。
查询出版最久的书的出版日期:
>>> Publisher.objects.aggregate(oldest_pubdate=Min(‘book__pubdate‘))
查询每个作者写的书的总页数:
>>> Author.objects.annotate(total_pages=Sum(‘book__pages‘))
查询所有作者写的书的平均rating:
>>> Author.objects.aggregate(average_rating=Avg(‘book__rating‘))
聚合和其它查询集操作Aggregations and other QuerySet clauses
filter() and exclude()
聚合可以和filter和exclude一起使用:
>>> from django.db.models import Count, Avg>>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count(‘authors‘))>>> Book.objects.filter(name__startswith="Django").aggregate(Avg(‘price‘))
可以根据聚合值进行筛选:
>>> Book.objects.annotate(num_authors=Count(‘authors‘)).filter(num_authors__gt=1)
顺序的不同,产生的意义也不同:
>>> 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‘))
都返回一个QuerySet,前者计算的是全部书的数量,后者只计算好书的数量。
order_by()
可以根据聚合值进行排序:
>>> Book.objects.annotate(num_authors=Count(‘authors‘)).order_by(‘num_authors‘)
values()
>>> Author.objects.values(‘name‘).annotate(average_rating=Avg(‘book__rating‘))
这样的写法下,QuerySet会根据name进行组合,返回的是每个unique name的聚合值。如果有两个作者有相同的名字,这两个作者会被当做一个计算,他们的books会合在一起。
>>> Author.objects.annotate(average_rating=Avg(‘book__rating‘)).values(‘name‘, ‘average_rating‘)
位置互换后,会为每个author都生成一个average_rating,而且只会输出每个author的name和average_rating。
默认排序下使用聚合:
from django.db import modelsclass Item(models.Model): name = models.CharField(max_length=10) data = http://www.mamicode.com/models.IntegerField()"name"]
如果想计算每个不同的data的数量,你可能这样写:
# Warning: 不正确的写法Item.objects.values("data").annotate(Count("id"))
因为query会根据(data,name)对进行组合,你需要这样写来去除默认排序的影响:
Item.objects.values("data").annotate(Count("id")).order_by()
Aggregating annotations
也可以根据annotation结果生成聚合值,例如计算每本书平均有几个作者:
>>> from django.db.models import Count, Avg>>> Book.objects.annotate(num_authors=Count(‘authors‘)).aggregate(Avg(‘num_authors‘)){‘num_authors__avg‘: 1.66}