Разница между методами аннотирования и агрегирования в Django?

115

У Django QuerySetесть два метода: annotateи aggregate. В документации сказано, что:

В отличие от aggregate (), annotate () не является терминальным предложением. Результатом предложения annotate () является QuerySet.

Есть ли еще какая-нибудь разница между ними? Если нет, то почему aggregateсуществует?

Александр Артеменко
источник

Ответы:

188

Я бы сосредоточился на примерах запросов, а не на вашей цитате из документации. Aggregateвычисляет значения для всего набора запросов. Annotateвычисляет итоговые значения для каждого элемента в наборе запросов.

агрегирование

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

Возвращает словарь, содержащий среднюю цену всех книг в наборе запросов.

аннотирование

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

q - это набор запросов книг, но каждая книга аннотирована с указанием количества авторов.

Алэсдэйр
источник
Правильно ли я, что только .annotate()на qs не попадает в db, а на вызове q[0].num_authors? Я предполагаю, что aggregateвсегда должен попадать в db, поскольку это терминальное предложение?
alias51
@ alias51, это действительно связано с исходным вопросом, поэтому я не думаю, что комментарии к вопросу восьмилетней давности - лучшее место, чтобы задать его. Если вы хотите проверить, когда выполняются запросы, вы можете проверитьconnection.queries . Подсказка: проверьте, является ли book = q[0]запрос вызывающим или `book.num_authors`.
Alasdair
21

В этом основное отличие, но агрегаты также работают в более широком масштабе, чем аннотации. Аннотации по своей сути связаны с отдельными элементами в наборе запросов. Если вы запустите Countаннотацию для чего-то вроде поля «многие ко многим», вы получите отдельный счетчик для каждого члена набора запросов (как добавленный атрибут). Однако, если бы вы сделали то же самое с агрегацией, она попыталась бы подсчитать все отношения для каждого члена набора запросов, даже дубликатов, и вернуть это как одно значение.

Крис Прэтт
источник
Правильно ли я, что только .annotate()на qs не попадает в db, а вызывает результат аннотации, например, q[0].num_authorsделает? Я предполагаю, что aggregateвсегда должен попадать в db, поскольку это терминальное предложение?
alias51
21

Aggregate Aggregate создает результирующие (сводные) значения для всего QuerySet. Aggregate работает с набором строк, чтобы получить одно значение из набора строк (например, сумма всех цен в наборе строк). Агрегат применяется ко всему QuerySet и генерирует результирующие (сводные) значения по всему QuerySet.

В модели:

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

В ракушке:

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

Annotate Annotate генерирует независимую сводку для каждого объекта в QuerySet. (Мы можем сказать, что он выполняет итерацию каждого объекта в QuerySet и применяет операцию)

В модели:

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

Ввиду:

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

В поле зрения он будет подсчитывать лайки для каждого видео

Винай Кумар
источник
почему distinct=Trueтребуется в последнем примере?
Юрий Леонов
@YuriyLeonov independent = True используется для того, чтобы операция выполнялась с отдельным значением. Это не связано с текущим заданным вопросом. Извините за это На самом деле я использовал свой код.
Винай Кумар,