Django的注释和聚合方法之间的区别?

Ale*_*nko 96 django django-queryset

Django QuerySet有两种方法,annotateaggregate.文件说:

与aggregate()不同,annotate()不是终结子句.annotate()子句的输出是QuerySet.

它们之间还有其他区别吗?如果没有,那么为什么aggregate存在?

Ala*_*air 158

我将专注于示例查询而不是文档中的引用.Aggregate计算整个查询集的值.Annotate计算查询集中每个项的汇总值.

聚合

>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}
Run Code Online (Sandbox Code Playgroud)

返回包含查询集中所有书籍平均价格的字典.

注解

>>> q = Book.objects.annotate(num_authors=Count('authors'))
>>> q[0].num_authors
2
>>> q[1].num_authors
1
Run Code Online (Sandbox Code Playgroud)

q 是书籍的查询集,但每本书都注明了作者的数量.

  • @alias51 这确实与原来的问题相关,所以我认为对一个八年前的问题的评论不是最好的提问地点。如果您想检查查询何时运行,那么您可以[检查`connection.queries`](/sf/ask/75194871/ django 正在运行)。提示:检查是`book = q[0]`还是`book.num_authors`引起了查询。 (2认同)

Chr*_*att 18

这是主要的区别,但聚合也比注释更宏大.注释本质上与查询集中的各个项相关.如果Count在类似多对多字段的内容上运行注释,则会为查询集的每个成员获取单独的计数(作为添加的属性).但是,如果要对聚合执行相同操作,它将尝试计算查询集的每个成员上的每个关系,即使是重复项,并将其作为一个值返回.


Vin*_*mar 12

Aggregate Aggregate在整个QuerySet上生成结果(摘要)值.聚合操作在行集上以从行集中获取单个值.(例如,行集中所有价格的总和).Aggregate应用于整个QuerySet,并在整个QuerySet上生成结果(摘要)值.

在模型中:

class Books(models.Model):
    name = models.CharField(max_length=100)
    pages = models.IntegerField()
    price = models.DecimalField(max_digits=5, decimal_places=3)
Run Code Online (Sandbox Code Playgroud)

在壳牌:

>>> Books.objects.all().aggregate(Avg('price'))
# Above code will give the Average of the price Column 
>>> {'price__avg': 34.35}
Run Code Online (Sandbox Code Playgroud)

Annotate Annotate为QuerySet中的每个对象生成一个独立的摘要.(我们可以说它迭代QuerySet中的每个对象并应用操作)

在模型中:

class Video(models.Model):
    name = models.CharField(max_length=52, verbose_name='Name')
    video = models.FileField(upload_to=document_path, verbose_name='Upload 
               video')
    created_by = models.ForeignKey(User, verbose_name='Created by', 
                       related_name="create_%(class)s")
    user_likes = models.ManyToManyField(UserProfile, null=True, 
                  blank=True, help_text='User can like once', 
                         verbose_name='Like by')
Run Code Online (Sandbox Code Playgroud)

在视图中:

videos = Video.objects.values('id', 'name','video').annotate(Count('user_likes',distinct=True)
Run Code Online (Sandbox Code Playgroud)

在视图中,它将计算每个视频的喜欢

  • 为什么上一个例子中需要 `distinct=True` ? (2认同)

小智 6

  • aggregate()可以计算当前模型列的所有值。*返回字典。

  • annotate()可以计算通过外键访问的子模型列的所有值。*返回一个查询集。

* Avg()Count()Max()Min()Sum()等可以与aggregate()and一起使用annotate()

例如有以下Category型号Product

# "models.py"

from django.db import models

class Category(models.Model):
    name = models.CharField(max_length=20)

class Product(models.Model):
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    name = models.CharField(max_length=50)
    price = models.DecimalField(decimal_places=2, max_digits=5)
Run Code Online (Sandbox Code Playgroud)

并且,还有Category以下Product管理员:

# "admin.py"

from django.contrib import admin
from .models import Category, Product 

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ('id', 'name')
    ordering = ('id',)

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ('id', 'category_id', 'category', 'name', 'price')
    ordering = ('id',)
Run Code Online (Sandbox Code Playgroud)

并且,有以下 2 类:

在此输入图像描述

并且,有以下 5 个产品:

在此输入图像描述

并且,有test下面的视图:

# "views.py"

from .models import Category, Product
from django.http import HttpResponse
from django.db.models import Avg
from django.db.models import Count
from django.db.models import Max
from django.db.models import Min
from django.db.models import Sum

def test(request):
    return HttpResponse("Test")
Run Code Online (Sandbox Code Playgroud)

首先,我解释一下aggregate()

总计的():

现在,我运行test具有columns的视图idcategorypriceAvg()Count()Max()Min()Sum()aggregate(),如下所示:

# "views.py"

# ...

def test(request):

    print(Product.objects.aggregate(Avg('id')))
    print(Product.objects.aggregate(Count('id')))
    print(Product.objects.aggregate(Max('id')))
    print(Product.objects.aggregate(Min('id')))
    print(Product.objects.aggregate(Sum('id')))
    print()
    print(Product.objects.aggregate(Avg('category')))
    print(Product.objects.aggregate(Count('category')))
    print(Product.objects.aggregate(Max('category')))
    print(Product.objects.aggregate(Min('category')))
    print(Product.objects.aggregate(Sum('category')))
    print()
    print(Product.objects.aggregate(Avg('price')))
    print(Product.objects.aggregate(Count('price')))
    print(Product.objects.aggregate(Max('price')))
    print(Product.objects.aggregate(Min('price')))
    print(Product.objects.aggregate(Sum('price')))
    
    return HttpResponse("Test")
Run Code Online (Sandbox Code Playgroud)

然后,以下这些字典将在控制台上输出:

{'id__avg': 3.0}
{'id__count': 5}
{'id__max': 5}
{'id__min': 1}
{'id__sum': 15}

{'category__avg': 1.4}
{'category__count': 5}
{'category__max': 2}
{'category__min': 1}
{'category__sum': 7}

{'price__avg': Decimal('30.0000000000000000')}
{'price__count': 5}
{'price__max': Decimal('50.00')}
{'price__min': Decimal('10.00')}
{'price__sum': Decimal('150.00')}
Run Code Online (Sandbox Code Playgroud)

并且,aggregate()可以接受任意顺序的多种列和函数、多种相同类型的列和函数以及无列和函数,如下所示。*将多个相同类型的列和函数合二为一,并且没有列和函数得到空字典:

# "views.py"

# ...

def test(request):
    # Multiple kinds of columns and functions in any order
    print(
        Product.objects.aggregate(
            Max('price'), Max('category'), Sum('id'), Min('id')
        )
    )

    # The multiple same kind of columns and functions
    print(
        Product.objects.aggregate(
            Sum('price'), Sum('price'), Sum('price')
        )
    )

    # No columns and functions
    print(Product.objects.aggregate())
        
    return HttpResponse("Test")
Run Code Online (Sandbox Code Playgroud)

然后,以下这些字典将在控制台上输出:

{'price__max': Decimal('50.00'), 'category__max': 2, 'id__sum': 15, 'id__min': 1}
{'price__sum': Decimal('150.00')}
{}
Run Code Online (Sandbox Code Playgroud)

并且,Max()以下Min()可以接受非数字类型

# "views.py"

# ...

def test(request):

    print(Product.objects.aggregate(Count('name')))
    print(Product.objects.aggregate(Max('name')))
    print(Product.objects.aggregate(Min('name')))
        
    return HttpResponse("Test")
Run Code Online (Sandbox Code Playgroud)

然后,以下这些字典将在控制台上输出:

{'name__count': 5}
{'name__max': 'Tea'}
{'name__min': 'Apple'}
Run Code Online (Sandbox Code Playgroud)

但是,Avg()以下Sum()不能接受非数字类型

# "views.py"

# ...

def test(request):

    print(Product.objects.aggregate(Avg('name')))
    print(Product.objects.aggregate(Sum('name')))
        
    return HttpResponse("Test")
Run Code Online (Sandbox Code Playgroud)

所以,就会出现下面的错误:

django.db.utils.ProgrammingError:函数 avg(字符变化)不存在

django.db.utils.ProgrammingError:函数 sum(字符变化)不存在

并且,您可以更改默认键名称,如下所示:

# "views.py"

# ...

def test(request):

    print(Product.objects.aggregate(priceAve=Avg('price')))
    print(Product.objects.aggregate(priceCount=Count('price')))
    print(Product.objects.aggregate(priceMax=Max('price')))
    print(Product.objects.aggregate(priceMin=Min('price')))
    print(Product.objects.aggregate(priceSum=Sum('price')))
        
    return HttpResponse("Test")
Run Code Online (Sandbox Code Playgroud)

然后,默认键名更改如下:

{'priceAve': Decimal('30.0000000000000000')}
{'priceCount': 5}
{'priceMax': Decimal('50.00')}
{'priceMin': Decimal('10.00')}
{'priceSum': Decimal('150.00')}
Run Code Online (Sandbox Code Playgroud)

接下来我就解释一下annotate()

注释():

现在,我运行test具有product__idproduct__categoryproduct__pricein Avg()Count()、 、Max()Min()inSum()的视图annotate(),如下所示。*您需要将__avg__count__max__min和分别__sum放入product__idproduct__categoryproduct__priceAvg()Count()Max()Min()Sum()

# "views.py"

# ...

def test(request):

    qs = Category.objects.annotate(
        Avg('product__id'), 
        Count('product__id'), 
        Max('product__id'), 
        Min('product__id'), 
        Sum('product__id')
    ).order_by('pk')

    for obj in qs:
        print(
            obj.id, 
            obj.name, 
            obj.product__id__avg, 
            obj.product__id__count, 
            obj.product__id__max, 
            obj.product__id__min, 
            obj.product__id__sum
        )

    print()

    qs = Category.objects.annotate(
        Avg('product__category'), 
        Count('product__category'), 
        Max('product__category'), 
        Min('product__category'), 
        Sum('product__category')
    ).order_by('pk')

    for obj in qs:
        print(
            obj.id, 
            obj.name, 
            obj.product__category__avg, 
            obj.product__category__count, 
            obj.product__category__max, 
            obj.product__category__min, 
            obj.product__category__sum
        )

    print()

    qs = Category.objects.annotate(
        Avg('product__price'), 
        Count('product__price'), 
        Max('product__price'), 
        Min('product__price'), 
        Sum('product__price')
    ).order_by('pk')

    for obj in qs:
        print(
            obj.id, 
            obj.name, 
            obj.product__price__avg, 
            obj.product__price__count, 
            obj.product__price__max, 
            obj.product__price__min, 
            obj.product__price__sum
        )
    
    return HttpResponse("Test")
Run Code Online (Sandbox Code Playgroud)

然后,在控制台上输出以下内容:

1 Food 2.0 3 3 1 6
2 Drink 4.5 2 5 4 9

1 Food 1.0 3 1 1 3
2 Drink 2.0 2 2 2 4

1 Food 20.0000000000000000 3 30.00 10.00 60.00
2 Drink 45.0000000000000000 2 50.00 40.00 90.00
Run Code Online (Sandbox Code Playgroud)

并且,下面不带order_by('pk') 的查询使订单后代:

# "views.py"

# ...

def test(request):

    qs = Category.objects.annotate(
        Avg('product__price'), 
        Count('product__price'), 
        Max('product__price'), 
        Min('product__price'), 
        Sum('product__price')
    ) # Without ".order_by('pk')"

    for obj in qs:
        print(
            obj.id, 
            obj.name, 
            obj.product__price__avg, 
            obj.product__price__count, 
            obj.product__price__max, 
            obj.product__price__min, 
            obj.product__price__sum
        )
    
    return HttpResponse("Test")
Run Code Online (Sandbox Code Playgroud)

然后,顺序是后代,如下所示:

2 Drink 4.5 2 5 4 9
1 Food 2.0 3 3 1 6
Run Code Online (Sandbox Code Playgroud)

并且,下面的空有annotate()idname属性:

# "views.py"

# ...

def test(request): 
                         # Empty "annotate()"
    qs = Category.objects.annotate().order_by('pk')

    for obj in qs:
        print(obj.id, obj.name)
        
    return HttpResponse("Test")
Run Code Online (Sandbox Code Playgroud)

然后,在控制台上输出以下内容:

1 Food
2 Drink
Run Code Online (Sandbox Code Playgroud)

但是,下面的emptyannotate()没有__avg__count__max__min__sum属性,如下所示:

# "views.py"

# ...

def test(request): 
                         # Empty "annotate()"
    qs = Category.objects.annotate().order_by('pk')
    
    for obj in qs:
        print(
            obj.product__price__avg,
            obj.product__price__count,
            obj.product__price__max,
            obj.product__price__min,
            obj.product__price__sum,
        )
        
    return HttpResponse("Test")
Run Code Online (Sandbox Code Playgroud)

所以,就会出现下面的错误:

AttributeError:“类别”对象没有属性“product__price__avg”

AttributeError:“类别”对象没有属性“product__price__count”

AttributeError:“类别”对象没有属性“product__price__max”

AttributeError:“类别”对象没有属性“product__price__min”

AttributeError:“类别”对象没有属性“product__price__sum”

并且,Max()以下Min()可以接受非数字类型

# "views.py"

# ...

def test(request):

    qs = Category.objects.annotate(
        Count('product__name'), 
        Max('product__name'), 
        Min('product__name'), 
    ).order_by('pk')

    for obj in qs:
        print( 
            obj.product__name__count, 
            obj.product__name__max, 
            obj.product__name__min, 
        )

    return HttpResponse("Test")
Run Code Online (Sandbox Code Playgroud)

然后,在控制台上输出以下内容:

3 Orange Apple
2 Tea Milk
Run Code Online (Sandbox Code Playgroud)

但是,Avg()以下Sum()不能接受非数字类型

# "views.py"

# ...

def test(request):

    qs = Category.objects.annotate(
        Avg('product__name'),
        Sum('product__name')
    ).order_by('pk')

    for obj in qs:
        print( 
            obj.product__name__avg, 
            obj.product__name__sum
        )
        
    return HttpResponse("Test")
Run Code Online (Sandbox Code Playgroud)

所以,就会出现下面的错误:

django.db.utils.ProgrammingError:函数 avg(字符变化)不存在

django.db.utils.ProgrammingError:函数 sum(字符变化)不存在

并且,您可以更改默认属性名称,如下所示:

# "views.py"

# ...

def test(request):

    qs = Category.objects.annotate(
        productPriceAvg=Avg('product__price'), 
        productPriceCount=Count('product__price'), 
        productPriceMax=Max('product__price'), 
        productPriceMin=Min('product__price'), 
        productPriceSum=Sum('product__price')
    ).order_by('pk')

    for obj in qs:
        print(
            obj.id, 
            obj.name, 
            obj.productPriceAvg, 
            obj.productPriceCount, 
            obj.productPriceMax, 
            obj.productPriceMin, 
            obj.productPriceSum
        )
    
    return HttpResponse("Test")
Run Code Online (Sandbox Code Playgroud)

然后,在控制台上输出以下内容:

1 Food 20.0000000000000000 3 30.00 10.00 60.00
2 Drink 45.0000000000000000 2 50.00 40.00 90.00
Run Code Online (Sandbox Code Playgroud)