Я использую модель транзакции, чтобы отслеживать все события, происходящие в системе
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
django
django-queryset
тоторо
источник
источник
Ответы:
Согласно документации, вы должны использовать:
from django.db.models import Count Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
values (): указывает, какие столбцы будут использоваться для "группировки по"
Документы Django:
annotate (): указывает операцию над сгруппированными значениями
Документы Django:
Порядок по пункту не требует пояснений.
Подводя итог: вы группируете, создавая набор запросов авторов, добавляете аннотацию (это добавит дополнительное поле к возвращаемым значениям) и, наконец, вы упорядочиваете их по этому значению
Обратитесь к https://docs.djangoproject.com/en/dev/topics/db/aggregation/ для получения дополнительной информации.
На заметку: при использовании Count значение, переданное в Count, не влияет на агрегацию, а только имя, присвоенное окончательному значению. Агрегатор группирует по уникальным комбинациям значений (как упомянуто выше), а не по значению, переданному в Count. Следующие запросы такие же:
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total') Transaction.objects.all().values('actor').annotate(total=Count('id')).order_by('total')
источник
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
, не забудьте импортировать Count из django.db.models. СпасибоCount
(и, возможно, других агрегаторов) значение, переданное вCount
, не влияет на агрегирование, а только имя, присвоенное окончательному значению. Агрегатор группирует по уникальным комбинациямvalues
(как упоминалось выше), а не по переданному значениюCount
.total
указано имя, а в sql используется счетчик,COUNT('actor')
который в этом случае не имеет значения, но если, напримерvalues('x', 'y').annotate(count=Count('x'))
, вы получитеCOUNT(x)
, нетCOUNT(*)
илиCOUNT(x, y)
просто попробовали это в./manage.py shell
Точно так же, как @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.
источник
.order_by()
спас меня отordering
меты.