详解Django的 annotate() 函数:对查询结果进行聚合

  • Post category:Python

下面是关于Django中annotate()函数的详细讲解。

annotate()函数的作用

annotate()函数的作用是给查询结果集的每一条记录增加一个聚合函数的注释,即对每个查询结果集合中的对象,都添加一个新的属性,这个属性的值是指定字段的聚合结果。使用annotate()函数可以在查询中计算出相关值,并使用返回的查询结果对象的新属性。

annotate()函数的使用方法

annotate()函数接受的参数是聚合函数,可以是类似于 Count、Avg、Sum 等函数,也可以是自定义的聚合函数,例如,我们可以使用 annotate() 和 Count() 求出每个分类下共有多少篇文章:

from django.db.models import Count
from myapp.models import Book

category_books = Book.objects.values('category__name').annotate(count=Count('id'))
for cb in category_books:
    print(cb['category__name'], cb['count'])

在这个例子中,我们用 values() 获取 category__name 字段,接下来使用 annotate() 和 Count() 获取文章数量,最终打印每个分类的名字和对应的数量。

除了使用 Count() 函数,annotate() 还可以使用许多其他的聚合函数,例如 Avg() 函数,求出每个分类下的平均分数:

from django.db.models import Avg
from myapp.models import Book

category_score_avg = Book.objects.values('category__name').annotate(score_avg=Avg('score'))
for csa in category_score_avg:
    print(csa['category__name'], csa['score_avg'])

在这个例子中,我们用 values() 获取 category__name 字段,接下来使用 annotate() 和 Avg() 函数获取平均分数,最终打印每个分类的名字和对应的平均分数。

annotate()函数的实例说明

下面还提供两个使用 annotate() 函数的实例,更好地说明它的使用方法。

实例1:按照分类分组,计算每个分类下的书名字母数量之和

from django.db.models import Sum
from myapp.models import Book

category_char_count = Book.objects.values('category__name').annotate(char_count=Sum('name__length'))
for ccc in category_char_count:
    print(ccc['category__name'], ccc['char_count'])

通过使用 annotate() 函数和 Sum() 函数,我们可以按照分类分组,计算每个分类下的书名字母数量之和,最终打印每个分类的名字和对应的字母数量之和。

实例2:按照出版年份分组,计算每年的销售总额和平均销售额

from django.db.models import Sum, Avg
from myapp.models import Book, Sale

year_sales = Book.objects.annotate(year=ExtractYear('publish_time')). \
    values('year').annotate(total_sales=Sum('book__sale__price'), avg_sales=Avg('book__sale__price'))
for ys in year_sales:
    print(ys['year'], ys['total_sales'], ys['avg_sales'])

在这个例子中,我们使用了 annotate() 函数、 ExtractYear() 函数、 Sum() 函数和 Avg() 函数,首先通过 ExtractYear() 函数获取图书出版的年份,接下来使用 annotate() 函数和 Sum() 函数获取销售总额,使用 annotate() 和 Avg() 函数获取平均销售额,最终打印每年的销售总额和平均销售额。