Voglio prendere le ultime 10 istanze di un modello e avere questo codice:
Model.objects.all().order_by('-id')[:10]
È vero che prima raccogli tutte le istanze e poi ne prendi solo 10 ultime? Esiste un metodo più efficace?
Voglio prendere le ultime 10 istanze di un modello e avere questo codice:
Model.objects.all().order_by('-id')[:10]
È vero che prima raccogli tutte le istanze e poi ne prendi solo 10 ultime? Esiste un metodo più efficace?
Risposte:
I queryset di Django sono pigri. Ciò significa che una query colpirà il database solo quando si richiede specificamente il risultato.
Pertanto, fino a quando non si stampa o non si utilizza effettivamente il risultato di una query, è possibile filtrare ulteriormente senza accesso al database.
Come puoi vedere di seguito, il tuo codice esegue solo una query sql per recuperare solo gli ultimi 10 elementi.
In [19]: import logging
In [20]: l = logging.getLogger('django.db.backends')
In [21]: l.setLevel(logging.DEBUG)
In [22]: l.addHandler(logging.StreamHandler())
In [23]: User.objects.all().order_by('-id')[:10]
(0.000) SELECT "auth_user"."id", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."password", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."is_superuser", "auth_user"."last_login", "auth_user"."date_joined" FROM "auth_user" ORDER BY "auth_user"."id" DESC LIMIT 10; args=()
Out[23]: [<User: hamdi>]
In realtà penso che LIMIT 10verrebbe emesso nel database in modo che il taglio non si verificherebbe in Python ma nel database.
Vedi limiter-queryset per maggiori informazioni.
Sembra che la soluzione nella domanda non funzioni più con Django 1.7 e genera un errore: "Impossibile riordinare una query una volta presa una porzione"
Secondo la documentazione https://docs.djangoproject.com/en/dev/topics/db/queries/#limiting-querysets forzando il parametro "step" della sintassi della slice Python valuta la query. Funziona in questo modo:
Model.objects.all().order_by('-id')[:10:1]
Mi chiedo comunque se il limite viene eseguito in sezioni SQL o Python restituite dall'intero array di risultati. Non è utile recuperare enormi elenchi nella memoria dell'applicazione.
Sì. Se vuoi recuperare un sottoinsieme limitato di oggetti, puoi farlo con il codice seguente:
Esempio:
obj=emp.objects.all()[0:10]
L'inizio 0 è facoltativo, quindi
obj=emp.objects.all()[:10]
Il codice sopra riportato restituisce le prime 10 istanze.
Come aggiunta e osservazione alle altre risposte utili, vale la pena notare che effettivamente fare [:10]come slicing restituirà i primi 10 elementi dell'elenco , non gli ultimi 10 ...
Per ottenere gli ultimi 10 dovresti [-10:]invece fare (vedi qui ). Questo vi aiuterà a evitare di utilizzare order_by('-id')il -per invertire gli elementi.
Product.objects.filter(~Q(price=0))[-5:]mi causa lo stesso errore: "L'indicizzazione negativa non è supportata."