Estou usando um modelo de transação para rastrear todos os eventos que ocorrem no sistema
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()
......
como faço para obter os 5 principais atores em meu sistema?
Em sql será basicamente
SELECT actor, COUNT(*) as total
FROM Transaction
GROUP BY actor
ORDER BY total DESC
django
django-queryset
Totoromeow
fonte
fonte
Respostas:
De acordo com a documentação, você deve usar:
valores (): especifica quais colunas serão usadas para "agrupar por"
Django docs:
annotate (): especifica uma operação sobre os valores agrupados
Django docs:
A cláusula order by é autoexplicativa.
Para resumir: você agrupa, gerando um conjunto de consultas de autores, adiciona a anotação (isso adicionará um campo extra aos valores retornados) e finalmente, você os ordena por este valor
Consulte https://docs.djangoproject.com/en/dev/topics/db/aggregation/ para obter mais informações
É bom observar: se estiver usando Count, o valor passado para Count não afeta a agregação, apenas o nome dado ao valor final. O agregador agrupa por combinações exclusivas dos valores (conforme mencionado acima), não pelo valor passado para Count. As seguintes consultas são iguais:
fonte
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
, não se esqueça de importar Count de django.db.models. ObrigadoCount
(e talvez outros agregadores), o valor passado paraCount
não afeta a agregação, apenas o nome dado ao valor final. O agregador agrupa por combinações exclusivas devalues
(como mencionado acima), não pelo valor passado paraCount
.total
é o nome fornecido e a contagem usada em sql éCOUNT('actor')
que, neste caso, não importa, mas sevalues('x', 'y').annotate(count=Count('x'))
, por exemplo , você obteráCOUNT(x)
, nãoCOUNT(*)
ouCOUNT(x, y)
, apenas tentei em./manage.py shell
Assim como @Alvaro respondeu ao equivalente direto do Django para a
GROUP BY
declaração:é por meio do uso dos métodos
values()
e daannotate()
seguinte maneira:No entanto, mais uma coisa deve ser apontada:
Se o modelo tiver uma ordenação padrão definida em
class Meta
, a.order_by()
cláusula é obrigatória para resultados adequados. Você simplesmente não pode ignorá-lo, mesmo quando nenhum pedido é pretendido.Além disso, para um código de alta qualidade, é aconselhável sempre colocar uma
.order_by()
cláusula apósannotate()
, mesmo quando não houverclass Meta: ordering
. Essa abordagem tornará a declaração à prova de futuro: ela funcionará exatamente como pretendido, independentemente de quaisquer alterações futuras emclass Meta: ordering
.Deixe-me dar um exemplo. Se o modelo tivesse:
Então, essa abordagem NÃO funcionaria:
Isso porque o Django executa mais
GROUP BY
em cada campo doclass Meta: ordering
Se você imprimir a consulta:
Ficará claro que a agregação NÃO funcionaria conforme o esperado e, portanto, a
.order_by()
cláusula deve ser usada para limpar esse comportamento e obter resultados de agregação adequados.Veja: Interação com ordenação padrão ou order_by () na documentação oficial do Django.
fonte
.order_by()
me salvou deordering
Meta.