2010-02-23 9 views
18

Hay, estoy usando django 1.2 y quiero saber cómo contar las filas de un queryset crudo (RawQuerySet).Django count RawQuerySet

El método tradicional .count() no funciona.

Heres mi consulta

query = "SELECT *, ((ACOS(SIN(%s * PI()/180) * SIN(lat * PI()/180) + COS(%s * PI()/180) * COS(lat * PI()/180) * COS((%s - lon) * PI()/180)) * 180/PI()) * 60 * 1.1515) AS distance FROM app_car WHERE price BETWEEN %s AND %s HAVING distance<=%s ORDER BY distance ASC" 

cars = Car.objects.raw(query, [lat, lat, lon, min_price, max_price, miles]) 

return HttpResponse(cars) 

Y su regreso

Car_Deferred_model_id_user_id object 

¿Alguna idea?

Respuesta

21

Utilice la función 'len()'. Esto daría:

query = "SELECT *, ((ACOS(SIN(%s * PI()/180) * SIN(lat * PI()/180) + COS(%s * PI()/180) * COS(lat * PI()/180) * COS((%s - lon) * PI()/180)) * 180/PI()) * 60 * 1.1515) AS distance FROM app_car WHERE price BETWEEN %s AND %s HAVING distance<=%s ORDER BY distance ASC" 

cars = Car.objects.raw(query, [lat, lat, lon, min_price, max_price, miles]) 

return HttpResponse(len(list(cars)) 

Aparte: hay algo de información útil en el método de Django 1.2 Model.objects.raw() en: http://djangoadvent.com/1.2/smoothing-curve/ [Parece que el sitio podría haber expirado, pero el Archivo de Internet tiene en: http://web.archive.org/web/20110513122309/http://djangoadvent.com/1.2/smoothing-curve/ ]

+3

Conseguir este error objeto de tipo 'RawQuerySet' no tiene len() – dotty

+8

len (lista (coches)) parece funcionar después de que echo el objeto como una lista – dotty

+0

OK, he actualizado la respuesta en consecuencia. – msanders

2

La razón por la que no hay 'recuento' es porque necesitaría una consulta adicional de "recuento (*)" en la base de datos para conocer el tamaño de su conjunto de resultados.

Tenga en cuenta que llamar al list(cars) está cargando todos sus resultados en la memoria. Esto le permite obtener el conteo con len, pero puede ser una operación costosa si tiene un gran conjunto de resultados.

+0

Esto solo funciona si la consulta devuelve al menos una fila, ya que de lo contrario el recuento no estará en ninguna parte :-) –

6

A decir verdad, si todo lo que quiere es el número total de registros en el RawQuerySet, entonces por supuesto, debe evitar convertir el RawQuerySet en una lista.

Al convertir RawQuerySet en una lista, se repetirá a través de cada registro que coincida con la consulta. Esto es potencialmente molesto para el servidor. Use recuento () en su lugar. Esto puede lograrse mediante el conteo envolvente () alrededor del SQL sin procesar que utilizó para engendrar el RawQuerySet.

I utilizado esto para resolver el problema:

def add_len_protocol_to_raw_sql_query(query): 
    """ 
    Adds/Overrides a dynamic implementation of the length protocol to the definition of RawQuerySet for the remainder of this thread's lifespan 
    """ 
    from django.db.models.query import RawQuerySet 
    def __len__(self): 
     from django.db import connection 
     sql = 'SELECT COUNT(*) FROM (' + query + ') B;' 
     cursor = connection.cursor() 
     cursor.execute(sql) 
     row = cursor.fetchone() 
     return row[ 0 ] 
    setattr(RawQuerySet, '__len__', __len__) 
query = 'SELECT * FROM A_TABLE_OF_MINE' 
add_len_protocol_to_raw_sql_query(query) 

Esto hace que una modificación dinámica para RawQuerySet de forma que responda al protocolo len().

Esto es mucho mejor en términos de rendimiento, tú que hay potencial para un inconveniente: si utiliza RawQuerySet más de una vez, entonces sería conveniente para descartar la dinámica _ len _ aplicación.

¿Alguno de ustedes sabe si el _ len _ método se verá limitado por el contexto de ejecución de la persona que llama? Si se usa MOD_WSGI en Apache, ¿significa esto que todos los hilos en el proceso de la persona que llama compartirán la definición modificada?

2

Aquí está la solución mejorada que se basa en la user871977:

from django.db import connection 

def get_len(rawqueryset): 
    def __len__(self): 
     params = ["""'%s'""" % p for p in self.params] 
     sql = 'SELECT COUNT(*) FROM (' + (rawqueryset.raw_query % tuple(params)) + ') B;' 
     cursor = connection.cursor() 
     cursor.execute(sql) 
     row = cursor.fetchone() 
     return row[0] 
    return __len__ 

rawqueryset = .... # a RawQuerySet instance 
setattr(type(rawqueryset), '__len__', get_len(rawqueryset)) 
+0

En lugar de insertar parámetros manualmente en la cadena sql, simplemente debe pasarlos a 'cursor.execute (sql, self.params) ' – serg

+0

@serg call' cursor.execute (sql, [params]) 'si está basado en el documento de Django en' https://docs.djangoproject.com/en/2.0/topics/db/sql/#executing-custom-sql -directly'. ¡Gracias! – caot