¿Diferencia entre los métodos de anotación y agregado de Django?

113

Django QuerySettiene dos métodos annotatey aggregate. La documentación dice que:

A diferencia de aggregate (), annotate () no es una cláusula terminal. La salida de la cláusula annotate () es un QuerySet.

¿Existe alguna otra diferencia entre ellos? Si no es así, ¿por qué aggregateexiste?

Alexander Artemenko
fuente

Respuestas:

186

Me centraría en las consultas de ejemplo en lugar de su cita de la documentación. Aggregatecalcula valores para todo el conjunto de consultas. Annotatecalcula valores de resumen para cada elemento del conjunto de consultas.

Agregación

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

Devuelve un diccionario que contiene el precio medio de todos los libros del conjunto de consultas.

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 consultas de libros, pero cada libro se ha anotado con el número de autores.

Alasdair
fuente
¿Estoy en lo cierto en que .annotate()en un qs solo no llega a la base de datos, pero llamar q[0].num_authorssí? Supongo aggregateque siempre debe presionar la base de datos, ya que es una cláusula terminal.
alias51
@ alias51 está realmente relacionado con la pregunta original, así que no creo que los comentarios sobre una pregunta de hace ocho años sean el mejor lugar para preguntar. Si desea comprobar cuándo se ejecutan las consultas, puede hacerloconnection.queries . Sugerencia: compruebe si es el book = q[0]o `book.num_authors` lo que provoca la consulta.
Alasdair
21

Esa es la principal diferencia, pero los agregados también funcionan a una escala mayor que las anotaciones. Las anotaciones están intrínsecamente relacionadas con elementos individuales en un conjunto de consultas. Si ejecuta una Countanotación en algo así como un campo de muchos a muchos, obtendrá un recuento por separado para cada miembro del conjunto de consultas (como un atributo agregado). Sin embargo, si hiciera lo mismo con una agregación, intentaría contar todas las relaciones en cada miembro del conjunto de consultas, incluso las duplicadas, y devolverlas como un solo valor.

Chris Pratt
fuente
¿Estoy en lo cierto en que solo .annotate()en una qs no llega a la base de datos, pero llamando al resultado de una anotación como lo q[0].num_authorshace? Supongo aggregateque siempre debe presionar la base de datos, ya que es una cláusula terminal.
alias51
21

Agregar agregado genera valores de resultado (resumen) en todo un QuerySet. El agregado opera 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 Shell:

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

Anotar Anotar generar un resumen independiente para cada objeto en un QuerySet. (Podemos decir que iterar cada objeto en un QuerySet y aplicar 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 me gusta de cada video.

Vinay Kumar
fuente
¿ distinct=TruePor qué se requiere en el último ejemplo?
Yuriy Leonov
@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