¿Usando .aggregate () en un valor introducido usando .extra (select = {…}) en una consulta de Django?

Estoy tratando de contar la cantidad de veces que un jugador jugó cada semana de la siguiente manera:

player.game_objects.extra( select={'week': 'WEEK(`games_game`.`date`)'} ).aggregate(count=Count('week')) 

Pero Django se queja de que

 FieldError: Cannot resolve keyword 'week' into field. Choices are:  

Puedo hacerlo en SQL crudo como este

 SELECT WEEK(date) as week, COUNT(WEEK(date)) as count FROM games_game WHERE player_id = 3 GROUP BY week 

¿Hay una buena manera de hacerlo sin ejecutar SQL en bruto en Django?

Podría usar una función agregada personalizada para producir su consulta:

 WEEK_FUNC = 'STRFTIME("%%%%W", %s)' # use 'WEEK(%s)' for mysql class WeekCountAggregate(models.sql.aggregates.Aggregate): is_ordinal = True sql_function = 'WEEK' # unused sql_template = "COUNT(%s)" % (WEEK_FUNC.replace('%%', '%%%%') % '%(field)s') class WeekCount(models.aggregates.Aggregate): name = 'Week' def add_to_query(self, query, alias, col, source, is_summary): query.aggregates[alias] = WeekCountAggregate(col, source=source, is_summary=is_summary, **self.extra) >>> game_objects.extra(select={'week': WEEK_FUNC % '"games_game"."date"'}).values('week').annotate(count=WeekCount('pk')) 

Pero como esta API no está documentada y ya requiere bits de SQL sin formato, puede que sea mejor utilizar una consulta sin formato .

Aquí hay un ejemplo del problema y una solución alternativa no ideal. Toma este ejemplo de modelo:

 class Rating(models.Model): RATING_CHOICES = ( (1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5'), ) rating = models.PositiveIntegerField(choices=RATING_CHOICES) rater = models.ForeignKey('User', related_name='ratings_given') ratee = models.ForeignKey('User', related_name='ratings_received') 

Esta consulta agregada de ejemplo falla de la misma manera que la suya porque intenta hacer referencia a un valor que no es de campo creado usando .extra() .

 User.ratings_received.extra( select={'percent_positive': 'ratings > 3'} ).aggregate(count=Avg('positive')) 

Una solución alternativa

El valor deseado se puede encontrar directamente usando la función de base de datos agregada (Promedio en este caso) dentro de la definición del valor adicional:

 User.ratings.extra( select={'percent_positive': 'AVG(rating >= 3)'} ) 

Esta consulta generará la siguiente consulta SQL:

 SELECT (AVG(rating >= 3)) AS `percent_positive`, `ratings_rating`.`id`, `ratings_rating`.`rating`, `ratings_rating`.`rater_id`, `ratings_rating`.`ratee_id` FROM `ratings_rating` WHERE `ratings_rating`.`ratee_id` = 1 

A pesar de las columnas innecesarias en esta consulta, aún podemos obtener el valor deseado de ella aislando el valor percent_positive :

 User.ratings.extra( select={'percent_positive': 'AVG(rating >= 3)'} ).values('percent_positive')[0]['percent_positive']