values - ¿Cómo hacer SELECT COUNT(*) GROUP BY y ORDER BY en Django?
django values aggregate (2)
Al igual que @Alvaro ha respondido el equivalente directo de Django para la declaración GROUP BY
:
SELECT actor, COUNT(*) AS total
FROM Transaction
GROUP BY actor
es mediante el uso de los métodos values()
y annotate()
siguiente manera:
Transaction.objects.values(''actor'').annotate(total=Count(''actor'')).order_by()
Sin embargo, una cosa más debe ser señalada:
Si el modelo tiene un orden predeterminado definido en la class Meta
, la cláusula .order_by()
es obligatoria para obtener los resultados adecuados. Simplemente no puede omitirlo incluso cuando no se pretende realizar un pedido.
Además, para un código de alta calidad, se aconseja colocar siempre una cláusula .order_by()
después de annotate()
, incluso cuando no haya ninguna class Meta: ordering
. Tal enfoque hará que la declaración sea a prueba del futuro: funcionará como se pretende, independientemente de cualquier cambio futuro en la class Meta: ordering
.
Déjame darte un ejemplo. Si el modelo tenía:
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''))
Esto se debe a que Django realiza GROUP BY
adicional en todos los campos de la class Meta: ordering
Si imprimiera 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"
.order_by()
claro que la agregación NO funcionará según lo previsto y, por lo tanto, la cláusula .order_by()
debe utilizarse para borrar este comportamiento y obtener los resultados de agregación adecuados.
Consulte: Interacción con el pedido predeterminado u order_by () en la documentación oficial de Django.
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 puedo obtener 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
De acuerdo con la documentación, debes usar:
from django.db.models import Count
Transaction.objects.all().values(''actor'').annotate(total=Count(''actor'')).order_by(''total'')
values (): especifica qué columnas se usarán para "agrupar por"
Documentos de Django:
"Cuando se utiliza una cláusula values () para restringir las columnas que se devuelven en el conjunto de resultados, el método para evaluar las anotaciones es ligeramente diferente. En lugar de devolver un resultado anotado para cada resultado en el QuerySet original, los resultados originales se agrupan de acuerdo a las combinaciones únicas de los campos especificados en la cláusula values () "
anotar (): especifica una operación sobre los valores agrupados
Documentos de Django:
La segunda forma de generar valores de resumen es generar un resumen independiente para cada objeto en un QuerySet. Por ejemplo, si está recuperando una lista de libros, es posible que desee saber cuántos autores contribuyeron a cada libro. Cada libro tiene una relación de muchos a muchos con el autor; queremos resumir esta relación para cada libro en QuerySet.
Los resúmenes por objeto se pueden generar utilizando la cláusula anotación (). Cuando se especifica una cláusula anotación (), cada objeto en el QuerySet se anotará con los valores especificados.
La cláusula order by se explica por sí misma.
Para resumir: agrupa por generación de un conjunto de consulta de autores, agregue la anotación (esto agregará un campo adicional a los valores devueltos) y, finalmente, los ordena por este valor
Consulte docs.djangoproject.com/en/dev/topics/db/aggregation para obtener más información