python - filtros - Ordene un QuerySet por valor de campo agregado
orm django (3)
Digamos que tengo el siguiente modelo:
class Contest:
title = models.CharField( max_length = 200 )
description = models.TextField()
class Image:
title = models.CharField( max_length = 200 )
description = models.TextField()
contest = models.ForeignKey( Contest )
user = models.ForeignKey( User )
def score( self ):
return self.vote_set.all().aggregate( models.Sum( ''value'' ) )[ ''value__sum'' ]
class Vote:
value = models.SmallIntegerField()
user = models.ForeignKey( User )
image = models.ForeignKey( Image )
Los usuarios de un sitio pueden contribuir con sus imágenes a varios concursos. Luego, otros usuarios pueden votar hacia arriba o hacia abajo. Todo funciona bien, pero ahora quiero mostrar una página en la que los usuarios puedan ver todas las contribuciones a un concurso determinado. Las imágenes se ordenarán por su puntuación. Por lo tanto, intenté lo siguiente:
Contest.objects.get( pk = id ).image_set.order_by( ''score'' )
Como temía, no funciona, ya que ''puntaje'' no es un campo de base de datos que pueda usarse en consultas.
Ah, por supuesto, me olvido del nuevo soporte de agregación en Django y su funcionalidad de annotate
.
Entonces la consulta puede verse así:
Contest.objects.get(pk=id).image_set.annotate(score=Sum(''vote__value'')).order_by( ''score'' )
El orden de nivel db por order_by
no puede ordenar el conjunto de preguntas por el método python del modelo.
La solución es introducir el campo de score
en el modelo de Image
y volver a calcularlo en cada actualización de Vote
. Algún tipo de desnormalización Cuando puedas, puedes ordenarlo por él.
Puedes escribir tu propio género en Python de manera muy simple.
def getScore( anObject ):
return anObject.score()
objects= list(Contest.objects.get( pk = id ).image_set)
objects.sort( key=getScore )
Esto funciona bien porque ordenamos la lista, que vamos a proporcionar a la plantilla.