簡體   English   中英

Django 中的 aggregate() 與 annotate()

[英]aggregate() vs annotate() in Django

Django 的QuerySet有兩個方法, annotateaggregate 文件說:

與 aggregate() 不同,annotate() 不是終止子句。 annotate()子句的output是一個QuerySet。 https://docs.djangoproject.com/en/4.1/topics/db/aggregation/#generating-aggregates-for-each-item-in-a-queryset

它們之間還有其他區別嗎? 如果不是,那么aggregate為什么存在?

我將重點關注示例查詢,而不是文檔中的引用。 Aggregate計算整個查詢 Aggregate值。 Annotate計算查詢集中每個項目的匯總值。

聚合

>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}

返回包含查詢集中所有書籍平均價格的字典。

注解

>>> q = Book.objects.annotate(num_authors=Count('authors'))
>>> q[0].num_authors
2
>>> q[1].num_authors
1

q是書籍的查詢集,但每本書都注明了作者的數量。

這是主要的區別,但聚合也比注釋更宏大。 注釋本質上與查詢集中的各個項相關。 如果在類似多對多字段的內容上運行Count注釋,則會為查詢集的每個成員獲取單獨的計數(作為添加的屬性)。 但是,如果要對聚合執行相同操作,它將嘗試計算查詢集的每個成員上的每個關系,即使是重復項,並將其作為一個值返回。

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)

在殼牌:

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

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')

在視圖中:

videos = Video.objects.values('id', 'name','video').annotate(Count('user_likes',distinct=True)

在視圖中,它將計算每個視頻的喜歡

  • aggregate()可以計算模型的列。 *返回字典。

  • annotate()可以計算相同外鍵的子模型的id列。

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

例如下面有CategoryProduct模型:

# "models.py"

from django.db import models

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

    def __str__(self):
        return self.name

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)
    
    def __str__(self):
        return self.name

並且,下面有CategoryProduct管理員:

# "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',)

並且,有以下兩類:

在此處輸入圖像描述

並且,有以下 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")

首先,我解釋一下aggregate()

總計的():

現在,我運行test視圖,該視圖在Avg()Count()Max()Min()Sum()中具有idcategory_idpriceaggregate()中,如下所示:

# "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('category_id')))
    print(Product.objects.aggregate(Count('category_id')))
    print(Product.objects.aggregate(Max('category_id')))
    print(Product.objects.aggregate(Min('category_id')))
    print(Product.objects.aggregate(Sum('category_id')))
    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")

然后,以下這些詞典在控制台上輸出:

{'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}

{'category_id__avg': 1.4}
{'category_id__count': 5}
{'category_id__max': 2}
{'category_id__min': 1}
{'category_id__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')}

並且, 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")

然后,以下這些詞典在控制台上輸出:

{'price__max': Decimal('50.00'), 'category__max': 2, 'id__sum': 15, 'id__min': 1}
{'price__sum': Decimal('150.00')}
{}

並且,下面的Max()Min()可以接受非數字類型:

# "views.py"

# ...

def test(request):

    print(Product.objects.aggregate(Max('name')))
    print(Product.objects.aggregate(Min('name')))
        
    return HttpResponse("Test")

然后,以下這些詞典在控制台上輸出:

{'name__max': 'Tea'}
{'name__min': 'Apple'}

但是,下面的Avg()Count()Sum()不能接受非數字類型:

# "views.py"

# ...

def test(request):

    print(Product.objects.aggregate(Avg('name')))
    print(Product.objects.aggregate(Count('name')))
    print(Product.objects.aggregate(Sum('name')))
        
    return HttpResponse("Test")

因此,會出現以下錯誤:

django.db.utils.ProgrammingError:function avg(字符變化)不存在

django.db.utils.ProgrammingError:function 總和(字符變化)不存在

django.db.utils.ProgrammingError:function 總和(字符變化)不存在

並且,您可以更改默認鍵名,如下所示:

# "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")

然后,默認鍵名更改如下所示:

{'priceAve': Decimal('30.0000000000000000')}
{'priceCount': 5}
{'priceMax': Decimal('50.00')}
{'priceMin': Decimal('10.00')}
{'priceSum': Decimal('150.00')}

接下來,我解釋一下annotate()

注釋():

現在,我在annotate()中的Count()中運行具有model 名稱producttest視圖,如下所示。 * model name product可以獲取Product model 中id列的值,需要將__avg , __count , __max , __min__sum product for Avg() , Count() , Max() , Min()Sum()分別是:

# "views.py"

# ...

def test(request):

    qs = Category.objects.annotate(Avg('product'))
    print(qs)
    print(qs[0].id, qs[0].name, qs[0].product__avg)
    print(qs[1].id, qs[1].name, qs[1].product__avg)

    qs = Category.objects.annotate(Count('product'))
    print(qs)
    print(qs[0].id, qs[0].name, qs[0].product__count)
    print(qs[1].id, qs[1].name, qs[1].product__count)

    qs = Category.objects.annotate(Max('product'))
    print(qs)
    print(qs[0].id, qs[0].name, qs[0].product__max)
    print(qs[1].id, qs[1].name, qs[1].product__max)

    qs = Category.objects.annotate(Min('product'))
    print(qs)
    print(qs[0].id, qs[0].name, qs[0].product__min)
    print(qs[1].id, qs[1].name, qs[1].product__min)

    qs = Category.objects.annotate(Sum('product'))
    print(qs)
    print(qs[0].id, qs[0].name, qs[0].product__sum)
    print(qs[1].id, qs[1].name, qs[1].product__sum)
    
    return HttpResponse("Test")

然后,在控制台上輸出以下內容:

<QuerySet [<Category: Food>, <Category: Drink>]>
1 Food 2.0 # Average "id" column in "Product" model whose row's foreign key is "1" of "Food"
2 Drink 4.5 # Average "id" column in "Product" model whose row's foreign key is "2" of "Drink"
<QuerySet [<Category: Food>, <Category: Drink>]>       
1 Food 3 # Count "id" in "Product" model whose row's foreign key is "1" of "Food"
2 Drink 2 # Count "id" in "Product" model whose row's foreign key is "2" of "Drink"
<QuerySet [<Category: Food>, <Category: Drink>]>       
1 Food 3 # Get the highest "id" from "id" column in "Product" model whose row's foreign key is "1" of "Food"
2 Drink 5 # Get the highest "id" from "id" column in "Product" model whose row's foreign key is "2" of "Drink"
<QuerySet [<Category: Food>, <Category: Drink>]>
1 Food 1 # Get the lowest "id" from "Product" model's "id" column whose row's foreign key is "1" of "Food"
2 Drink 4 # Get the lowest "id" from "Product" model's "id" column whose row's foreign key is "2" of "Drink"
<QuerySet [<Category: Food>, <Category: Drink>]>       
1 Food 6 # Sum "id" column in "Product" model whose row's foreign key is "1" of "Food"
2 Drink 9 # Sum "id" column in "Product" model whose row's foreign key is "2" of "Drink"

並且, annotate()可以接受多種函數,如下所示:

# "views.py"

# ...

def test(request):
    # Multiple kinds of functions
    qs = Category.objects.annotate(
        Avg('product'), 
        Count('product'), 
        Max('product'), 
        Min('product'), 
        Sum('product')
    )
    print(qs)
    print(
        qs[0].id, qs[0].name, 
        qs[0].product__avg, 
        qs[0].product__count, 
        qs[0].product__max, 
        qs[0].product__min, 
        qs[0].product__sum
    )
    print(
        qs[1].id, qs[1].name, 
        qs[1].product__avg, 
        qs[1].product__count, 
        qs[1].product__max, 
        qs[1].product__min, 
        qs[1].product__sum
    )
        
    return HttpResponse("Test")

然后,在控制台上輸出以下內容:

<QuerySet [<Category: Food>, <Category: Drink>]>
1 Food 2.0 3 3 1 6
2 Drink 4.5 2 5 4 9

而且,下面的annotate()沒有productproduct_id屬性:

# "views.py"

# ...

def test(request):
    qs = Category.objects.annotate(
        Avg('product'), 
        Count('product'), 
        Max('product'), 
        Min('product'), 
        Sum('product')
    )
    print(qs[0].product) # Here
    print(qs[1].product_id) # Here
        
    return HttpResponse("Test")

因此,會出現以下錯誤:

AttributeError: 'Category' object 沒有屬性 'product'

AttributeError: 'Category' object 沒有屬性 'product_id'

並且,下面的annotate()具有idname屬性:

# "views.py"

# ...

def test(request):
    qs = Category.objects.annotate() # Empty "annotate()"
    print(qs[0].id, qs[0].name) # Here
    print(qs[1].id, qs[1].name) # Here
        
    return HttpResponse("Test")

然后,在控制台上輸出以下內容:

1 Food
2 Drink

但是,下面的annotate()沒有product__avgproduct__countproduct__maxproduct__minproduct__sum屬性:

# "views.py"

# ...

def test(request):
    qs = Category.objects.annotate() # Empty "annotate()"
    print(
        qs[0].product__avg, # Here
        qs[0].product__count, # Here
        qs[0].product__max, # Here
        qs[0].product__min, # Here
        qs[0].product__sum # Here
    )
    print(
        qs[1].product__avg, # Here
        qs[1].product__count, # Here
        qs[1].product__max, # Here
        qs[1].product__min, # Here
        qs[1].product__sum # Here
    )
        
    return HttpResponse("Test")

因此,會出現以下錯誤:

AttributeError: 'Category' object 沒有屬性 'product__avg'

AttributeError: 'Category' object 沒有屬性 'product__count'

AttributeError: 'Category' object 沒有屬性 'product__max'

AttributeError: 'Category' object 沒有屬性 'product__min'

AttributeError: 'Category' object 沒有屬性 'product__sum'

並且,您可以更改默認屬性名稱,如下所示:

# "views.py"

# ...

def test(request):

    qs = Category.objects.annotate(productAvg=Avg('product'))
    print(qs)
    print(qs[0].id, qs[0].name, qs[0].productAvg)
    print(qs[1].id, qs[1].name, qs[1].productAvg)

    qs = Category.objects.annotate(productCount=Count('product'))
    print(qs)
    print(qs[0].id, qs[0].name, qs[0].productCount)
    print(qs[1].id, qs[1].name, qs[1].productCount)

    qs = Category.objects.annotate(productMax=Max('product'))
    print(qs)
    print(qs[0].id, qs[0].name, qs[0].productMax)
    print(qs[1].id, qs[1].name, qs[1].productMax)

    qs = Category.objects.annotate(productMin=Min('product'))
    print(qs)
    print(qs[0].id, qs[0].name, qs[0].productMin)
    print(qs[1].id, qs[1].name, qs[1].productMin)

    qs = Category.objects.annotate(productSum=Sum('product'))
    print(qs)
    print(qs[0].id, qs[0].name, qs[0].productSum)
    print(qs[1].id, qs[1].name, qs[1].productSum)
    
    return HttpResponse("Test")

然后,在控制台上輸出以下內容:

<QuerySet [<Category: Food>, <Category: Drink>]>
1 Food 2.0
2 Drink 4.5
<QuerySet [<Category: Food>, <Category: Drink>]>
1 Food 3
2 Drink 2
<QuerySet [<Category: Food>, <Category: Drink>]>
1 Food 3
2 Drink 5
<QuerySet [<Category: Food>, <Category: Drink>]>
1 Food 1
2 Drink 4
<QuerySet [<Category: Food>, <Category: Drink>]>
1 Food 6
2 Drink 9

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM