Как выбрать COUNT (*) GROUP BY и ORDER BY в Django?
Я использую модель транзакций для отслеживания всех событий, проходящих через систему
class Transaction(models.Model):
actor = models.ForeignKey(User, related_name="actor")
acted = models.ForeignKey(User, related_name="acted", null=True, blank=True)
action_id = models.IntegerField()
......
Как получить ТОП 5 актеров в моей системе?
в sql это будет в основном
SELECT actor, COUNT(*) as total
FROM Transaction
GROUP BY actor
ORDER BY total DESC
2 ответов
согласно документации, вы должны использовать:
from django.db.models import Count
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
values (): указывает, какие столбцы будут использоваться для "group by"
Django docs:
" когда предложение values() используется для ограничения столбцов, которые возвращаемых в результирующем наборе, метод оценки аннотаций немного разный. Вместо возврата аннотированного результата для каждого результат в исходном наборе запросов исходные результаты группируются в соответствии с уникальными комбинациями полей, указанных в предложение values ()"
annotate (): указывает операцию над сгруппированными значениями
Django docs:
второй способ создания сводных значений-создание независимой сводки для каждого объекта в наборе запросов. Например, если вы извлекая список книг, вы можете узнать, сколько авторов внес вклад в каждую книгу. Каждая книга имеет много-ко-многим отношение с автором; мы хотим суммировать эти отношения для каждой книги в QuerySet.
сводки по объектам могут быть созданы с помощью предложения annotate (). Если указано предложение annotate (), каждый объект в наборе запросов будет аннотироваться указанными значениями.
предложение order by не требует пояснений.
суммировать: вы группируете, генерируя набор запросов авторов, добавляете аннотацию (это добавит дополнительную поле к возвращаемым значениям) и, наконец, вы заказываете их по этому значению
смотрите https://docs.djangoproject.com/en/dev/topics/db/aggregation/ для большего понимания
так же, как @Alvaro ответил на прямой эквивалент Django для GROUP BY
о себе:
SELECT actor, COUNT(*) AS total
FROM Transaction
GROUP BY actor
С помощью values()
и annotate()
методы следующим образом:
Transaction.objects.values('actor').annotate(total=Count('actor')).order_by()
однако следует отметить еще одну вещь:
если модель имеет порядок по умолчанию, определенный в class Meta
на .order_by()
положение является обязательным для правильных результатов. вы просто не можете пропустить его, даже если нет заказа предназначенный.
далее, для высококачественного кода рекомендуется всегда ставить .order_by()
п. После annotate()
, даже когда нет class Meta: ordering
. Такой подход сделает утверждение перспективным: оно будет работать так, как и предполагалось, независимо от любых будущих изменений class Meta: ordering
.
позвольте мне привести вам пример. Если бы у модели было:
class Transaction(models.Model):
actor = models.ForeignKey(User, related_name="actor")
acted = models.ForeignKey(User, related_name="acted", null=True, blank=True)
action_id = models.IntegerField()
class Meta:
ordering = ['id']
тогда такой подход не сработал бы:
Transaction.objects.values('actor').annotate(total=Count('actor'))
это потому, что Django выполняет дополнительные GROUP BY
на каждом поле в class Meta: ordering
если вы напечатаете запрос:
>>> print Transaction.objects.values('actor').annotate(total=Count('actor')).query
SELECT "Transaction"."actor_id", COUNT("Transaction"."actor_id") AS "total"
FROM "Transaction"
GROUP BY "Transaction"."actor_id", "Transaction"."id"
будет ясно, что агрегация не будет работать по назначению, и поэтому .order_by()
предложение должно использоваться для очистки этого поведения и получения правильных результатов агрегации.
посмотреть: взаимодействие с заказом по умолчанию или order_by () в официальной документации Django.