Alejandro Artemenko
de Django QuerySet
tiene dos métodos, annotate
y aggregate
. La documentación dice que:
A diferencia de added(), annotate() no es una cláusula terminal. El resultado de la cláusula annotate() es un QuerySet.
https://docs.djangoproject.com/en/4.1/topics/db/aggregation/#generando-agregados-para-cada-elemento-en-un-conjunto-de-consulta
¿Hay alguna otra diferencia entre ellos? Si no, entonces ¿por qué aggregate
¿existir?
Me centraría en las consultas de ejemplo en lugar de su cita de la documentación. Aggregate
calcula los valores de completo conjunto de consultas Annotate
calcula los valores de resumen para Cada artículo en el conjunto de consultas.
Agregación
>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}
Devuelve un diccionario que contiene el precio promedio de todo libros en el conjunto de consulta.
Anotación
>>> q = Book.objects.annotate(num_authors=Count('authors'))
>>> q[0].num_authors
2
>>> q[1].num_authors
1
q
es el conjunto de consulta de libros, pero cada libro se ha anotado con el número de autores.
-
¿Estoy en lo correcto?
.annotate()
en un qs solo no golpea el db, pero llamandoq[0].num_authors
¿hace? Asumoaggregate
siempre debe golpear el db ya que es una cláusula terminal?– alias51
30 de julio de 2020 a las 23:05
-
@alias51 que está realmente relacionado con la pregunta original, por lo que no creo que los comentarios sobre una pregunta de hace ocho años sean el mejor lugar para preguntar. Si desea verificar cuándo se ejecutan las consultas, puede verificar
connection.queries
. Pista: compruebe si es elbook = q[0]
o ` book.num_authors` que provoca la consulta.– Alasdair
31 de julio de 2020 a las 12:27
Vinay Kumar
Agregar
Agregue valores de resultados de generación (resumen) en un QuerySet completo. La operación agregada sobre el conjunto de filas para obtener un valor único del conjunto de filas (por ejemplo, la suma de todos los precios en el conjunto de filas). El agregado se aplica en todo el QuerySet y genera valores de resultado (resumen) en todo un QuerySet.
En modelo:
class Books(models.Model):
name = models.CharField(max_length=100)
pages = models.IntegerField()
price = models.DecimalField(max_digits=5, decimal_places=3)
En cáscara:
>>> Books.objects.all().aggregate(Avg('price'))
# Above code will give the Average of the price Column
>>> {'price__avg': 34.35}
Anotar
Anotar genera un resumen independiente para cada objeto en un QuerySet. (Podemos decir que itera cada objeto en un QuerySet y aplica la operación)
En modelo:
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")
En vista:
videos = Video.objects.values('id', 'name','video').annotate(Count('user_likes',distinct=True)
A la vista contará los likes de cada vídeo
-
por qué
distinct=True
se requiere en el último ejemplo?– Yuri Leonov
8 de abril de 2020 a las 13:34
-
@YuriyLeonov distinto=Verdadero utilizado para que la operación se realice en un valor distinto. No está relacionado con la pregunta actual. Lo siento por eso, en realidad lo he usado en mi código.
– Vinay Kumar
8 de abril de 2020 a las 13:48
Esa es la principal diferencia, pero los agregados también funcionan a mayor escala que las anotaciones. Las anotaciones están inherentemente relacionadas con elementos individuales en un conjunto de consultas. Si ejecuta un Count
anotación en algo así como un campo de muchos a muchos, obtendrá un recuento separado para cada miembro del conjunto de consultas (como un atributo agregado). Sin embargo, si tuviera que hacer lo mismo con una agregación, intentaría contar cada relación en cada miembro del conjunto de consultas, incluso duplicados, y lo devuelve como un solo valor.
-
¿Estoy en lo correcto?
.annotate()
en un qs solo no golpea el db, sino que llama al resultado de una anotación comoq[0].num_authors
¿hace? Asumoaggregate
siempre debe golpear el db ya que es una cláusula terminal?– alias51
30 de julio de 2020 a las 23:07
Kai-Kazuya Ito
-
agregar() can calcula todos los valores de la columna de un modelo. *Se devuelve un diccionario.
-
anotar() puede calcular todas las claves externas de la columna de un modelo secundario por clave externa.
*Promedio(), Contar(), máx(), Min(), Suma() y así sucesivamente se puede utilizar con aggregate()
y annotate()
.
por ejemplo, hay Category
y Product
modelos a continuación:
# "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)
Y aquí están Category
y Product
administradores a continuación:
# "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',)
Y, hay 2 categorías a continuación:
Y, hay 5 productos a continuación:
Y ahí está test
ver a continuación:
# "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")
Primero, explico sobre aggregate()
.
agregar():
Ahora, corro test
vista que tiene las columnas id
, category
y price
en Promedio(), Contar(), máx(), Min() y Suma() en aggregate()
Como se muestra abajo:
# "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")
Luego, estos diccionarios a continuación se muestran en la consola:
{'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')}
Y, aggregate()
puede aceptar varios tipos de columnas y funciones en cualquier orden, los mismos tipos de columnas y funciones y ninguna columna ni función, como se muestra a continuación. * Las múltiples columnas y funciones del mismo tipo se convierten en una y ninguna columna y función obtiene un diccionario vacío:
# "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")
Luego, estos diccionarios a continuación se muestran en la consola:
{'price__max': Decimal('50.00'), 'category__max': 2, 'id__sum': 15, 'id__min': 1}
{'price__sum': Decimal('150.00')}
{}
Y, Max()
y Min()
a continuación puede aceptar tipos no numéricos:
# "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")
Luego, estos diccionarios a continuación se muestran en la consola:
{'name__count': 5}
{'name__max': 'Tea'}
{'name__min': 'Apple'}
Pero, Avg()
y Sum()
a continuación no puede aceptar tipos no numéricos:
# "views.py"
# ...
def test(request):
print(Product.objects.aggregate(Avg('name')))
print(Product.objects.aggregate(Sum('name')))
return HttpResponse("Test")
Por lo tanto, se producen los siguientes errores:
django.db.utils.ProgrammingError: la función avg (carácter variable) no existe
django.db.utils.ProgrammingError: la función sum (caracter variable) no existe
Y puede cambiar los nombres de clave predeterminados como se muestra a continuación:
# "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")
Luego, los nombres de clave predeterminados se cambian como se muestra a continuación:
{'priceAve': Decimal('30.0000000000000000')}
{'priceCount': 5}
{'priceMax': Decimal('50.00')}
{'priceMin': Decimal('10.00')}
{'priceSum': Decimal('150.00')}
A continuación, explico sobre annotate()
.
anotar():
Ahora, corro test
vista que tiene las columnas product__id
, product__category
y product__price
en Avg()
, Count()
, Max()
, Min()
y Sum()
en annotate()
Como se muestra abajo. * Tienes que poner __avg
, __count
, __max
, __min
y __sum
a product__id
, product__category
y product__price
para Avg()
, Count()
, Max()
, Min()
y Sum()
respectivamente:
# "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")
Luego, estos a continuación se emiten en la consola:
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
Y, la consulta sin order_by(‘pk’) a continuación hace que el orden sea descendiente:
# "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")
Entonces, el orden es descendiente como se muestra a continuación:
2 Drink 4.5 2 5 4 9
1 Food 2.0 3 3 1 6
Y, vacío annotate()
abajo tiene id
y name
atributos:
# "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")
Luego, estos a continuación se emiten en la consola:
1 Food
2 Drink
Pero, vacío annotate()
abajo no tiene __avg
, __count
, __max
, __min
y __sum
atributos como se muestra a continuación:
# "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")
Por lo tanto, se producen los siguientes errores:
AttributeError: el objeto ‘Categoría’ no tiene el atributo ‘product__price__avg’
AttributeError: el objeto ‘Categoría’ no tiene el atributo ‘product__price__count’
AttributeError: el objeto ‘Categoría’ no tiene el atributo ‘product__price__max’
AttributeError: el objeto ‘Categoría’ no tiene el atributo ‘product__price__min’
AttributeError: el objeto ‘Categoría’ no tiene el atributo ‘product__price__sum’
Y, Max()
y Min()
a continuación puede aceptar tipos no numéricos:
# "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")
Luego, estos a continuación se emiten en la consola:
3 Orange Apple
2 Tea Milk
Pero, Avg()
y Sum()
a continuación no puede aceptar tipos no numéricos:
# "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")
Por lo tanto, se producen los siguientes errores:
django.db.utils.ProgrammingError: la función avg (carácter variable) no existe
django.db.utils.ProgrammingError: la función sum (caracter variable) no existe
Y puede cambiar los nombres de atributos predeterminados como se muestra a continuación:
# "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")
Luego, estos a continuación se emiten en la consola:
1 Food 20.0000000000000000 3 30.00 10.00 60.00
2 Drink 45.0000000000000000 2 50.00 40.00 90.00
¿Puede agregar el enlace a esta declaración en los documentos?
–Lev Slinsen
20/04/2022 a las 21:15
enlace agregado a documentos
–Harry Moreno
10 ene a las 20:43