2015-06-04 5 views
5

Ho questa parte del codice nel mio API che recentemente è diventato un po 'collo di bottiglia:Django: come contare un queryset e restituire una slice senza colpire DB due volte?

total = results.count() 
if request.GET.has_key('offset'): 
    offset = int(request.GET.get('offset').strip()) 
    results = results.order_by('name')[100*offset:100*(offset+1)] 
people = list(results) 

Nota che results è il set di query di tutte le persone e offset è un parametro utilizzato per l'impaginazione.

Qui posso vedere, quando stampo connection.queries, che il mio database viene colpito due volte da .count() e list(results). Il motivo per cui .count() deve essere al top perché ho bisogno della lunghezza di tutte le persone (non 100.) C'è un modo per aggirare questo?

+2

Dove esattamente stai usando 'total'? – Jkdc

+1

[MySQL può farlo] (https://stackoverflow.com/a/2439870/1248008), ma non so come utilizzarlo utilizzando Django e non sono sicuro che altri database supportino questo. –

+0

@JonasWielicki È discutibile se ciò acceleri anche molto le cose; vedere: https://www.percona.com/blog/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/ - un modo migliore per risolvere il problema qui potrebbe essere capire innanzitutto perché quelle query sono mettendoci troppo tempo. Indici appropriati possono farti fare molto. –

risposta

0

Forse qualcosa di simile ?:

allpeople = list(results.order_by('name')) 
total = len(allpeople) 
if request.GET.has_key('offset'): 
    offset = int(request.GET.get('offset').strip()) 
    results = allpeople[100*offset:100*(offset+1)] 
people = results 

Tenete a mente che con people = results sta per fallire se if request.GET....: non si attiva.

+1

Potresti inizializzare risultati come 'results = allpeople = list (risultati ...' nella tua prima linea per rimuovere la necessità del tuo avvertimento finale –

+5

Non sono d'accordo con questa risposta perché non conosciamo il numero di risultati che vengono restituiti.Avvolgere un oggetto QuerySet in 'list() 'causa la sua valutazione e porta tutti i risultati in memoria.Se hai diversi milioni di risultati, questo può essere catastrofico Questo mi è successo di recente, quindi parlo per esperienza: –

+0

Hai ragione: è più lento rispetto a milioni di record, ma risponde totalmente alla domanda di evitare di colpire due volte il DB. .. Si è rivelato @ThomasOrozco aveva ragione che meno queri potrebbe non essere sempre più veloce. A proposito, l'indice 'name' è sempre stato lì fin dall'inizio. –

Problemi correlati