2013-04-23 10 views
10

C'erano alcuni esempi di codice per l'impaginazione del django che ho usato qualche tempo fa. Potrei sbagliarmi ma guardando il codice sembra che sprechi tonnellate di memoria. Ero alla ricerca di una soluzione migliore, ecco il codice:Impaginazione efficiente e interrogazione del database in django

# in views.py 
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger 

... 
...  

def someView(): 
    models = Model.objects.order_by('-timestamp') 
    paginator = Paginator(models, 7) 
    pageNumber = request.GET.get('page') 

    try: 
     paginatedPage = paginator.page(pageNumber) 
    except PageNotAnInteger: 
     pageNumber = 1 
    except EmptyPage: 
     pageNumber = paginator.num_pages 
    models = paginator.page(pageNumber) 

    return render_to_resp (..... models ....) 

Non sono sicuro delle subtlties di questo codice, ma da quello che sembra, la prima riga di codice recupera ogni singolo modello dal database e lo spinge dentro Quindi viene passato in Paginator che lo suddivide in base alla pagina in cui si trova l'utente da un GET html. In qualche modo, paginator lo rende accettabile, oppure è totalmente inefficiente? Se è inefficiente, come può essere migliorato?

Inoltre, un argomento correlato. Se qualcuno fa:

Model.objects.all()[:40] 

Questo codice significa che tutti i modelli sono spinti nella memoria, e noi splice fuori 40 di loro? Che è cattivo O significa che interrogiamo e spingiamo solo 40 oggetti nel periodo di memoria?

Grazie per il vostro aiuto!

risposta

16

mymodel.objects.all() produce un queryset, non un elenco. I set di query sono pigri: nessuna richiesta viene emessa e nulla viene fatto finché non si tenta effettivamente di usarli. Affettare anche un set di query non carica tutta la dannata cosa in memoria solo per ottenere un sottoinsieme ma aggiunge limite e offset alla query SQL prima di colpire il database.

-1

Non è presente memoria inefficiente quando si utilizza l'impaginatore. I querysets sono valutati pigramente. Nella vostra chiamata Paginator(models, 7), models è un queryset che non è stato valutato fino a questo punto. Quindi, fino ad ora il database non è stato colpito. Inoltre, nessuna lista contenente tutte le istanze del modello è in memoria a questo punto.

Quando si desidera ottenere una pagina, ad esempio paginatedPage = paginator.page(pageNumber), l'affettatura viene eseguita su questo set di query, solo a questo punto il database viene colpito e il database restituisce un set di query contenente istanze di modello. E quindi slicing restituisce solo gli oggetti che dovrebbero essere lì sulla pagina. Quindi, solo gli oggetti tagliati andranno in una lista che sarà lì nella memoria. Dì su una pagina che vuoi mostrare 10 oggetti, solo questi 10 oggetti rimarranno nella memoria.

Quando qualcuno lo fa;

Model.objects.all()[:40] 

Quando si suddivide un elenco, viene creato un nuovo elenco. Nel tuo caso verrà creato un elenco con solo 40 elementi e verrà memorizzato da qualche parte nella memoria. Nessun'altra lista ci sarà e quindi non ci sarà alcuna lista che contenga tutte le istanze di Model in memoria.

+0

Sì, ma i modelli sono tutti gli oggetti del modello nell'intero database, sono caricati proprio nella prima riga. Stai dicendo che paginator lo uccide o qualcosa del genere? Per la seconda risposta, sì, viene creata una nuova lista ma quella vecchia è ancora lì, giusto? O semplicemente scompare? –

+0

Per la seconda risposta, poiché il conteggio dei riferimenti per la vecchia lista sarà 0, sarà garbage collection quando si verifica la garbage collection poiché non è possibile raggiungere la vecchia lista. –

+0

Per la prima risposta, sì, i modelli sono tutti gli oggetti del modello nel database e saranno in memoria per l'intero periodo della visualizzazione. E tu lo vuoi diversamente, come si suppone che Django sappia quali oggetti mostrare su una determinata pagina? Django può farlo solo se gli fornisci tutti gli oggetti e poi gli dici quale pagina vuoi e poi django eseguirà l'affettamento e ti restituirà una nuova lista. –

0

Utilizzando le informazioni di cui sopra, mi sono inventato un decoratore di funzioni di visualizzazione. Json_list_objects prende gli oggetti djanog in doni python pronti per json dei campi di relazione noti degli oggetti django e restituisce l'elenco jsonified come {count: results:}.

Altri potrebbero trovare utile.

def with_paging(fn): 
    """ 
    Decorator providing paging behavior. It is for decorating a function that 
    takes a request and other arguments and returns the appropriate query 
    doing select and filter operations. The decorator adds paging by examining 
    the QueryParams of the request for page_size (default 2000) and 
    page_num (default 0). The query supplied is used to return the appropriate 
    slice. 
    """ 
    @wraps(fn) 
    def inner(request, *args, **kwargs): 
    page_size = int(request.GET.get('page_size', 2000)) 
    page_num = int(request.GET.get('page_num', 0)) 
    query = fn(request, *args, **kwargs) 
    start = page_num * page_size 
    end = start + page_size 
    data = query[start:end] 
    total_size = query.count() 
    return json_list_objects(data, overall_count=total_size) 
    return inner