Ayuda con la consulta ORM de Django

Tengo estos dos modelos de Django (simplificados):

class Movie(models.Model):
    title = models.CharField(max_length=255)
    year = models.IntegerField(max_length=4)
    rating = models.DecimalField(max_digits=2, decimal_places=1, default=0)


class Request(models.Model):
    movie = models.ForeignKey(Movie)
    user = models.ForeignKey(User)
    language = models.CharField(max_length=7, choices=settings.APP_LANGUAGES, db_index=True)
    notes = models.CharField(max_length=255, blank=True, null=True)
    added = models.DateTimeField(default=datetime.now)

Quiero obtener todas las solicitudes con un recuento por idioma para cada película. De hecho, se me ocurre una solución con una consulta ORM, que me da los resultados que estoy buscando:

reqs = Request.objects.values('movie', 'langauge').annotate(Count('language'))

Sin embargo, necesitaré acceso a otros atributos de película mientras renderizo los resultados en la plantilla. Obviamente, la consulta anterior devuelve ValueQuerySet como:

[{'movie': 460, 'language__count': 1, 'language': u'es-mx'}, {'movie': 458, 'language__count': 2, 'language': u'cs'}, {'movie': 459, 'language__count': 1, 'language': u'el'}]

Por lo tanto, algo como reqs[0].movie.rating no funcionará. Entonces, ¿cómo debo acceder a otros atributos de película?

Gracias por su atención.

preguntado el 16 de mayo de 11 a las 17:05

¿Se supone que SubtitleRequest es el mismo modelo que Request? -

sí, perdón por esta inconsistencia. Reparado -

Entonces, ¿hay algún problema con reqs = Request.objects.annotate(Count('language')).select_related('movie')? -

@Mike DeSimon, el problema con su consulta es que no se agrupa por idioma, es decir, obtendría language__count 1 para todos los resultados -

1 Respuestas

Puedes hacer algo como esto:

reqs = Request.objects.values('movie', 'movie__rating', 'movie__title', 'movie__year', 'language').annotate(Count('language'))
print reqs

aquí está la salida de muestra:

[
    {'movie__year': 2010, 'language': u'EN', 'movie__title': u'Foo', 'movie': 1, 'language__count': 1, 'movie__rating': Decimal('1')}, 
    {'movie__year': 2010, 'language': u'ES', 'movie__title': u'Foo', 'movie': 1, 'language__count': 1, 'movie__rating': Decimal('1')}, 
    {'movie__year': 2010, 'language': u'RU', 'movie__title': u'Foo', 'movie': 1, 'language__count': 1, 'movie__rating': Decimal('1')}, 
    {'movie__year': 1998, 'language': u'EN', 'movie__title': u'Bar', 'movie': 2, 'language__count': 3, 'movie__rating': Decimal('9')}, 
    {'movie__year': 1998, 'language': u'RU', 'movie__title': u'Bar', 'movie': 2, 'language__count': 1, 'movie__rating': Decimal('9')}, 
]

puede ver que devolver atributos de película adicionales no cambia el recuento, aquí está el resultado de muestra de su consulta original:

reqs = Request.objects.values('movie', 'language').annotate(Count('language'))
print reqs

esto produce salida:

[
    {'movie': 1, 'language__count': 1, 'language': u'EN'}, 
    {'movie': 1, 'language__count': 1, 'language': u'ES'}, 
    {'movie': 1, 'language__count': 1, 'language': u'RU'}, 
    {'movie': 2, 'language__count': 3, 'language': u'EN'}, 
    {'movie': 2, 'language__count': 1, 'language': u'RU'}, 
]

EDITAR

Si desea poder llamar a métodos en el película entonces quieres seleccionar el películas con tu consulta, no con el solicitudes :

movies = Movie.objects.annotate(num_lang=Count('request__language')).all()

Esto devolverá todas las películas, anotadas por el recuento de idiomas de la solicitud, a las que puede acceder a través de la propiedad * num_lang * en la película. También podrá llamar a todos los métodos en los objetos de películas.

contestado el 19 de mayo de 11 a las 07:05

No, porque todos esos campos se considerarán parte del GROUP BY. - Daniel Roseman

@Daniel Roseman, en realidad eso funciona muy bien. La razón es que la 'película' devuelve la identificación, que ya identifica de forma única a la película. Si devolvemos algún atributo adicional para la película, no afectará al "agrupar por". He agregado una salida de muestra. - Sergey Golovchenko

De acuerdo, esto es bastante bueno, parece funcionar de la manera que necesito. Pero, ¿hay alguna forma de que yo también pueda acceder a los métodos de película? Supongo que necesitaría usar reqs = Request.objects.values('movie', 'language').annotate(Count('language')) y luego seleccione Películas donde id__in=[r.movie for r in reqs], y luego de alguna manera mapearlos juntos. Pero no sé cuál sería una forma óptima de hacer tal mapeo, ¿alguna idea? - Daniel Grezo

@danger, he actualizado mi respuesta para mostrar cómo puede llamar a métodos de película. Espero que esto ayude. - Sergey Golovchenko

Gracias; Estuve allí, pero ese en realidad no devuelve cuenta para language y todavía necesito acceso al valor del lenguaje sin hacer consultas N db (supongo que select_related () no funcionaría allí). Así que sigo pensando que necesito hacer mi mapeo manualmente y aún no he llegado a algo óptimo. - Daniel Grezo

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.