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

Ernesto Savoretti esavoretti en gmail.com
Mie Ene 19 02:58:04 ART 2011


2011/1/19 Patricio Molina <patriciomolina en gmail.com>:
> Buenas lista, tengo un problemita con el orden de un QuerySet en Django
> (1.1) que les paso a describir.
> Tengo un modelo "Saraza" y un modelo "Rating". Lo relevante de "Rating" es
> que tiene un campo "score", que es un PositiveSmallIntegerField(). Hasta acá
> nada raro.
> El modelo "Rating", a su vez, tiene una relación "muchos a uno" con el
> modelo Saraza (saraza = models.ForeignKey(Saraza))
> En Saraza tengo un custom manager llamado SarazaManager, este es el código:
> class SarazaManager(models.Manager):
>     """
>     Custom manager for Saraza model
>     """
>     def get_query_set(self):
>         return super(SarazaManager, self).get_query_set()\
>             .annotate(rating=Avg('rating__score'),
> score_count=Count('rating__score'))\
>             .order_by('-rating', 'name')
> Como ven, lo que hace un par de agregaciones que las anota como atributos de
> cada objeto del modelo (.rating y .score_count, uno es un Avg() y el otro un
> Count())
> 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.
Veamos:

>>> int(None)

Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    int(None)
TypeError: int() argument must be a string or a number, not 'NoneType'

Parece que Python "no sabe" como transformar a None en un entero.
Probemos con float (tu caso):

>>> float(None)

Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    float(None)
TypeError: float() argument must be a string or a number

mmmhhhh. Tampoco.

Si sabe, en cambio:

>>> bool(None)
False

Pero lamentáblemente tu lista no es de booleanos, es de floats, por lo
cual le estás pidiendo algo que no sabe como hacer. Especulativamente,
puede que al no tener forma de transformarlo, lo considere como un
indeterminismo matemático y por lo tanto lo homologue a infinito y por
eso esté adelante. Puede ser cualquier cosa, pero cláramente el
problema es que estás intentando comparar entidades de dimensiones
distintas.

y/o cómo puedo solucionarlo?

> In [2]: [s.rating if s.rating else 0.0 for s in Saraza.objects.all()]
(si es que realmente querés que None equivalga a 0.0)

Algo al estilo de:
>>> [x if x else "@" for x in [1.0, 3.2, "lala", 0.0, 41, None, 7]]
[1.0, 3.2, 'lala', '@', 41, '@', 7]

O, si en vez de list comprehensions preferís/te resulta más claro usar map:
>>> map(lambda x: x if x else "@", [1.0, 3.2, "lala", 0.0, 41, None, 7])
[1.0, 3.2, 'lala', '@', 41, '@', 7]

Saludos

-- 

                                    Ernesto Savoretti



More information about the pyar mailing list