Diferença entre os métodos de anotação e agregação do Django?

115

O Django QuerySettem dois métodos, annotatee aggregate. A documentação diz que:

Ao contrário de aggregate (), annotate () não é uma cláusula terminal. A saída da cláusula annotate () é um QuerySet.

Existe alguma outra diferença entre eles? Se não, por que aggregateexiste?

Alexander Artemenko
fonte

Respostas:

187

Eu me concentraria nas consultas de exemplo, em vez de sua citação da documentação. Aggregatecalcula valores para todo o queryset. Annotatecalcula os valores de resumo para cada item no queryset.

Agregação

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

Retorna um dicionário contendo o preço médio de todos os livros no queryset.

Anotação

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

q é o conjunto de livros, mas cada livro foi anotado com o número de autores.

Alasdair
fonte
Estou correto que .annotate()em um qs sozinho não atinge o db, mas chamar q[0].num_authorssim? Presumo que aggregatedeve sempre acertar o db, pois é uma cláusula terminal?
alias51
@ alias51 que está realmente relacionado à pergunta original, então não acho que os comentários sobre uma pergunta de oito anos seja o melhor lugar para fazer. Se você quiser verificar quando as consultas são executadas, você pode verificarconnection.queries . Dica: verifique se é o book = q[0]ou `book.num_authors` que causa a consulta.
Alasdair
21

Essa é a principal diferença, mas os agregados também funcionam em uma escala maior do que as anotações. As anotações são inerentemente relacionadas a itens individuais em um queryset. Se você executar uma Countanotação em algo como um campo muitos-para-muitos, você obterá uma contagem separada para cada membro do queryset (como um atributo adicionado). Se você fizesse o mesmo com uma agregação, entretanto, ela tentaria contar todos os relacionamentos em cada membro do queryset, até mesmo duplicatas, e retornaria isso como apenas um valor.

Chris Pratt
fonte
Estou correto que .annotate()em um qs sozinho não atinge o db, mas chama o resultado de uma anotação como o q[0].num_authorsfaz? Presumo que aggregatedeve sempre acertar o db, pois é uma cláusula terminal?
alias51
21

Agregado Agregado gera valores de resultado (resumo) sobre um QuerySet inteiro. Aggregate operar sobre o conjunto de linhas para obter um único valor do conjunto de linhas. (Por exemplo, soma de todos os preços no conjunto de linhas). O agregado é aplicado em todo o QuerySet e gera valores de resultado (resumo) em todo o QuerySet.

No modelo:

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

Em Shell:

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

Annotate Annotate gera um resumo independente para cada objeto em um QuerySet. (Podemos dizer que itera cada objeto em um QuerySet e aplica a operação)

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

Em vista:

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

Em vista irá contar os gostos para cada vídeo

Vinay Kumar
fonte
por que distinct=Trueé necessário no último exemplo?
Yuriy Leonov
@YuriyLeonov various = True usado para que a operação seja executada em um valor distinto. Não está relacionado à pergunta atual feita. Desculpe por isso Na verdade eu usei no meu código.
Vinay Kumar