Estoy usando un modelo de transacción para realizar un seguimiento de todos los eventos que pasan por el 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()
......
¿Cómo obtengo los 5 mejores actores en mi sistema?
En sql básicamente será
SELECT actor, COUNT(*) as total
FROM Transaction
GROUP BY actor
ORDER BY total DESC
django
django-queryset
totoromeow
fuente
fuente
Respuestas:
Según la documentación, debe utilizar:
from django.db.models import Count Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
valores (): especifica qué columnas se utilizarán para "agrupar por"
Documentos de Django:
annotate (): especifica una operación sobre los valores agrupados
Documentos de Django:
El orden por cláusula se explica por sí mismo.
Para resumir: agrupa por, generando un conjunto de consultas de autores, agrega la anotación (esto agregará un campo adicional a los valores devueltos) y finalmente, los ordena por este valor
Consulte https://docs.djangoproject.com/en/dev/topics/db/aggregation/ para obtener más información.
Es bueno tener en cuenta: si usa Count, el valor pasado a Count no afecta la agregación, solo el nombre dado al valor final. El agregador agrupa por combinaciones únicas de los valores (como se mencionó anteriormente), no por el valor pasado a Count. Las siguientes consultas son las mismas:
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total') Transaction.objects.all().values('actor').annotate(total=Count('id')).order_by('total')
fuente
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
, no olvides importar Count desde django.db.models. GraciasCount
(y tal vez otros agregadores), el valor pasado aCount
no afecta la agregación, solo el nombre dado al valor final. El agregador agrupa por combinaciones únicas devalues
(como se mencionó anteriormente), no por el valor pasado aCount
.total
se da el nombre y el recuento utilizado en sql es loCOUNT('actor')
que en este caso no importa, pero sivalues('x', 'y').annotate(count=Count('x'))
, por ejemplo , obtendráCOUNT(x)
, noCOUNT(*)
oCOUNT(x, y)
, simplemente lo probó?./manage.py shell
Al igual que @Alvaro ha respondido el equivalente directo de Django para la
GROUP BY
declaración:SELECT actor, COUNT(*) AS total FROM Transaction GROUP BY actor
es mediante el uso de
values()
yannotate()
métodos de la siguiente manera:Transaction.objects.values('actor').annotate(total=Count('actor')).order_by()
Sin embargo, hay que señalar una cosa más:
Si el modelo tiene un orden predeterminado definido en
class Meta
, la.order_by()
cláusula es obligatoria para obtener resultados adecuados. Simplemente no puede omitirlo incluso cuando no se pretende realizar un pedido.Además, para un código de alta calidad, se recomienda colocar siempre una
.order_by()
cláusula despuésannotate()
, incluso cuando no hayclass Meta: ordering
. Dicho enfoque hará que la declaración esté preparada para el futuro: funcionará tal como se pretendía, independientemente de cualquier cambio futuro enclass Meta: ordering
.Déjame darte un ejemplo. Si el modelo tuviera:
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']
Entonces tal enfoque NO funcionaría:
Transaction.objects.values('actor').annotate(total=Count('actor'))
Eso es porque Django realiza más
GROUP BY
en cada campo enclass Meta: ordering
Si imprime la consulta:
>>> 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"
Quedará claro que la agregación NO funcionaría según lo previsto y, por lo tanto, la
.order_by()
cláusula debe usarse para borrar este comportamiento y obtener los resultados de agregación adecuados.Ver: Interacción con el orden predeterminado o order_by () en la documentación oficial de Django.
fuente
.order_by()
me salvóordering
en Meta.