Différence entre les méthodes d'annotation et d'agrégation de Django?

115

Django QuerySeta deux méthodes, annotateet aggregate. La documentation dit que:

Contrairement à aggregate (), annotate () n'est pas une clause terminale. La sortie de la clause annotate () est un QuerySet.

Y a-t-il une autre différence entre eux? Sinon, pourquoi aggregateexiste-t-il?

Alexandre Artemenko
la source

Réponses:

187

Je me concentrerais sur les requêtes d'exemple plutôt que sur votre citation de la documentation. Aggregatecalcule les valeurs de l' ensemble de requêtes. Annotatecalcule les valeurs récapitulatives pour chaque élément de l'ensemble de requêtes.

Agrégation

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

Renvoie un dictionnaire contenant le prix moyen de tous les livres du jeu de requête.

Annotation

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

q est l'ensemble de requêtes des livres, mais chaque livre a été annoté avec le nombre d'auteurs.

Alasdair
la source
Ai-je raison de dire que .annotate()sur un qs seul ne touche pas la base de données, mais l'appel le q[0].num_authorsfait? Je suppose que aggregatedoit toujours frapper la base de données car il s'agit d'une clause terminale?
alias51
@ alias51 qui est vraiment lié à la question originale, donc je ne pense pas que les commentaires sur une question de huit ans soient le meilleur endroit pour poser. Si vous souhaitez vérifier quand les requêtes s'exécutent, vous pouvez vérifierconnection.queries . Astuce: vérifiez si c'est le book = q[0]ou `book.num_authors` qui provoque la requête.
Alasdair
21

C'est la principale différence, mais les agrégats fonctionnent également à une plus grande échelle que les annotations. Les annotations sont intrinsèquement liées aux éléments individuels d'un ensemble de requêtes. Si vous exécutez une Countannotation sur quelque chose comme un champ plusieurs-à-plusieurs, vous obtiendrez un décompte distinct pour chaque membre de l'ensemble de requêtes (en tant qu'attribut ajouté). Si vous deviez faire de même avec une agrégation, cependant, il essaierait de compter chaque relation sur chaque membre de l'ensemble de requêtes, même les doublons, et de renvoyer cela comme une seule valeur.

Chris Pratt
la source
Ai-je raison de dire que .annotate()sur un qs seul ne frappe pas la base de données, mais appelle le résultat d'une annotation comme le q[0].num_authorsfait? Je suppose que aggregatedoit toujours frapper la base de données car il s'agit d'une clause terminale?
alias51
21

Agrégat Aggregate génère des valeurs de résultat (récapitulatives) sur un ensemble de requêtes entier. L'agrégation opère sur l'ensemble de lignes pour obtenir une valeur unique de l'ensemble de lignes (par exemple, la somme de tous les prix de l'ensemble de lignes). L'agrégat est appliqué à l'ensemble de QuerySet et génère des valeurs de résultat (récapitulatif) sur l'ensemble d'un QuerySet.

Dans le modèle:

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

Dans Shell:

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

Annoter Annoter générer un résumé indépendant pour chaque objet dans un QuerySet (nous pouvons dire qu'il itère chaque objet dans un QuerySet et applique une opération)

Dans le modèle:

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 vue:

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

En vue, il comptera les likes pour chaque vidéo

Vinay Kumar
la source
pourquoi distinct=Trueest requis dans le dernier exemple?
Yuriy Leonov le
@YuriyLeonov distinct = True utilisé pour que l'opération soit effectuée sur une valeur distincte. Ce n'est pas lié à la question actuelle posée. Désolé pour cela En fait, j'ai utilisé mon code.
Vinay Kumar le