2010-02-23 3 views
18

Hay, sto usando django 1.2 e voglio sapere come contare le righe da un queryset non elaborato (RawQuerySet).Conteggio Django RawQuerySet

Il metodo tradizionale .count() non funziona.

Heres la mia domanda

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) 

E il suo ritorno

Car_Deferred_model_id_user_id object 

Tutte le idee?

risposta

21

Utilizzare la funzione 'len()'. Questo darebbe:

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)) 

parte: c'è alcune informazioni utili sul metodo Django 1.2 Model.objects.raw() a: http://djangoadvent.com/1.2/smoothing-curve/ [Sembra che il sito potrebbe essere scaduta, ma l'Internet Archive ha all'indirizzo: http://web.archive.org/web/20110513122309/http://djangoadvent.com/1.2/smoothing-curve/ ]

+3

Ottenere questo errore oggetto di tipo 'RawQuerySet' non ha alcun len() – dotty

+8

len (lista (auto)) sembra funzionare dopo io scaccio l'oggetto come elenco – dotty

+0

OK, ho aggiornato la risposta di conseguenza. – msanders

2

Il motivo per cui non esiste un "conteggio" è perché è necessaria una query aggiuntiva "conteggio (*)" nel database per conoscere la dimensione del set di risultati.

Quindi tieni presente che chiamare list(cars) sta caricando tutti i tuoi risultati in memoria. Ciò consente di ottenere il conteggio con len, ma può essere un'operazione costosa se si dispone di un set di risultati di grandi dimensioni.

+0

Funziona solo se la query restituisce almeno una riga, altrimenti il ​​conteggio non sarà ovunque :-) –

6

A dire il vero, se tutto ciò che si desidera è il numero totale di record in RawQuerySet, è quindi consigliabile evitare di eseguire il casting di RawQuerySet in un elenco.

Il cast di RawQuerySet in un elenco scorre ogni record corrispondente alla query. Questo è potenzialmente oneroso per il server. Utilizzare invece il conteggio (). Ciò può essere ottenuto contornando il conteggio () attorno all'SQL grezzo utilizzato per generare il RawQuerySet.

Ho usato a risolvere il 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) 

Questo apporta una modifica dinamica RawQuerySet modo che risponde al protocollo len().

Questo è molto meglio in termini di prestazioni, tu esiste la possibilità di uno svantaggio: se si utilizza RawQuerySet più di una volta, allora sarebbe auspicabile eliminare la dinamica _ len _ attuazione.

Qualcuno di voi sa se il _ len _ metodo sarà vincolato dal contesto di esecuzione del chiamante? Se si utilizza MOD_WSGI su Apache, significa che tutti i thread nel processo del chiamante condivideranno la definizione modificata?

2

Ecco la soluzione migliore che si basa su user871977 di:

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

Invece di inserire manualmente parametri nella stringa sql, dovresti semplicemente passarli in 'cursor.execute (sql, self.params) ' – serg

+0

@serg call' cursor.execute (sql, [params]) 'se basato su Django doc su' https://docs.djangoproject.com/en/2.0/topics/db/sql/#executing-custom-sql -directly'. Grazie! – caot