Różnica między metodami adnotacji i agregacji w Django?

115

Django QuerySetma dwie metody annotatei aggregate. Dokumentacja mówi, że:

W przeciwieństwie do agregacji (), annotate () nie jest klauzulą ​​końcową. Dane wyjściowe klauzuli annotate () to QuerySet.

Czy jest między nimi jakaś inna różnica? Jeśli nie, to dlaczego aggregateistnieje?

Alexander Artemenko
źródło

Odpowiedzi:

187

Skupiłbym się na przykładowych zapytaniach, a nie na cytacie z dokumentacji. Aggregateoblicza wartości dla całego zestawu zapytań. Annotateoblicza wartości podsumowania dla każdego elementu w zestawie zapytań.

Zbiór

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

Zwraca słownik zawierający średnią cenę wszystkich książek w zestawie zapytań.

Adnotacja

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

q to zbiór książek, ale każda książka została opatrzona adnotacją z liczbą autorów.

Alasdair
źródło
Czy mam rację, że .annotate()na samym qs nie trafia do db, ale wywołanie q[0].num_authorstak? Zakładam, że aggregatezawsze musi trafić do bazy danych, ponieważ jest to klauzula terminala?
alias 51
@ alias51 jest naprawdę związane z pierwotnym pytaniem, więc nie sądzę, aby komentarze do pytania, które miało osiem lat, były najlepszym miejscem do zadawania. Jeśli chcesz sprawdzić, kiedy zapytania są uruchamiane, możesz to sprawdzićconnection.queries . Wskazówka: sprawdź, czy to book = q[0]lub `book.num_authors` powoduje zapytanie.
Alasdair
21

To główna różnica, ale agregaty działają również na większą skalę niż adnotacje. Adnotacje są nieodłącznie powiązane z pojedynczymi elementami w zestawie zapytań. Jeśli uruchomisz Countadnotację na czymś takim jak pole wiele do wielu, otrzymasz oddzielną liczbę dla każdego elementu członkowskiego zestawu zapytań (jako dodatkowy atrybut). Gdybyś jednak zrobił to samo z agregacją, spróbowałaby policzyć każdą relację na każdym elemencie zestawu zapytań, nawet duplikatów, i zwrócić ją jako jedną wartość.

Chris Pratt
źródło
Czy mam rację, że .annotate()na samym qs nie trafia do db, ale wywołuje wynik adnotacji, takiej jak q[0].num_authorsrobi? Zakładam, że aggregatezawsze musi trafić do bazy danych, ponieważ jest to klauzula terminala?
alias 51
21

Aggregate Aggregate generuje wartości wyników (podsumowania) w całym QuerySet. Agregacja działa na zestawie wierszy, aby uzyskać pojedynczą wartość z zestawu wierszy (na przykład suma wszystkich cen w zestawie wierszy). Agregat jest stosowany do całego zestawu QuerySet i generuje wartości wyniku (podsumowania) w całym zestawie QuerySet.

W modelu:

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

W skorupkach:

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

Annotate Annotate generuje niezależne podsumowanie dla każdego obiektu w QuerySet (możemy powiedzieć, że iteruje każdy obiekt w QuerySet i stosuje operację)

W modelu:

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

Z uwagi:

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

W związku z tym policzy polubienia każdego filmu

Vinay Kumar
źródło
dlaczego distinct=Truejest to wymagane w ostatnim przykładzie?
Yuriy Leonov
@YuriyLeonov odrębny = True używany do wykonywania operacji na odrębnej wartości. Nie ma to związku z aktualnym pytaniem. Przepraszam za to. Właściwie użyłem na moim kodzie.
Vinay Kumar