[pyar] [Django] order_by() de un QuerySet no está funcionando como pretendo

Patricio Molina patriciomolina en gmail.com
Mie Ene 19 12:57:32 ART 2011


2011/1/19 Pablo Ziliani <pablo en kultroom.com>:
> On 01/19/2011 02:58 AM, Ernesto Savoretti wrote:
>>
>> 2011/1/19 Patricio Molina<patriciomolina en gmail.com>:
>>>
>>> Vean lo que me devuelve cuando hago un query:
>>> In [1]: from saraza.models import Saraza
>>> In [2]: [s.rating for s in Saraza.objects.all()]
>>> Out[2]: [None, 4.0, 3.0]
>>> Uno de los objetos del modelo Saraza no tiene ningún objeto del modelo
>>> Rating asociado, por eso el rating es "None". No lo está ordenando como
>>> yo
>>> quiero: lo que pretendo es que vaya realmente en orden descendente (por
>>> ejemplo: [4.0, 3.0, ..., None, None, None] y no [None, None, None, 4.0,
>>> 3.0,
>>> ...] como hace ahora.
>>> Alguna idea de por qué sucede esto
>>
>> Bueno, me parece que en todo caso el problema es que pareciera que
>> estás asumiendo que: None = 0.0, lo cual no parece ser el caso.
>>
>> (...)
>>
>> y/o cómo puedo solucionarlo?
>>
>>> In [2]: [s.rating if s.rating else 0.0 for s in Saraza.objects.all()]
>
> El problema es que estás haciendo en python algo que sería mejor que haga la
> BBDD, además de que el resultado probablemente no le sirva (probablemente en
> la página quiera tener disponibles todos los datos de los Saraza(), no solo
> el rating)
>
> Fijate esta solución, me pareció suficientemente elegante:
> http://stackoverflow.com/questions/2572201/django-orm-ordering-w-aggregate-functions-none-special-treatment

Gracias Pablo. Casualmente ayer encontré esa solución (y un par más
muy similares) en Stack Overflow, pero tuve que hacerle algunas
modificaciones para poder implementarla porque mi consulta es más
compleja (estoy haciendo un Avg() basado en una relación foránea, no
sobre un campo de la tabla misma como hacen en ese ejemplo).

Como dice el tipo que publicó la respuesta, "Not 100% ORM in the
strictest sense but it does push the sorting back into the DB."

Ojalá que en futuras versiones de Django los order_by() que devuelva
agregaciones con None los ordene como hace el list(). La otra era
extender y cambiar el comportamiento de la clase Avg(), pero eso
hubiera sido muchísimo más complicado de mantener.

Acá está el resultado:

class SarazaManager(models.Manager):
    """
    Custom manager for Saraza model
    """
    def get_query_set(self):
        return super(SarazaManager, self).get_query_set().select_related()\
            .annotate(rating=Avg('rating__score'),
score_count=Count('rating__score'))\
            .extra(select={'has_rating': '(SELECT COUNT(*) > 0 FROM
"sarazas_rating" WHERE "sarazas_rating"."saraza_id" =
"sarazas_saraza"."id")'})\
            .order_by('-has_rating', '-rating', 'name')

In [1]: from sarazas.models import Saraza

In [2]: [e.rating for e in Saraza.objects.all()]
Out[2]: [5.0, 4.0, None]

Saludos



More information about the pyar mailing list