Django - Begrenzung der Abfrageergebnisse

200

Ich möchte die letzten 10 Instanzen eines Modells nehmen und diesen Code haben:

 Model.objects.all().order_by('-id')[:10]

Stimmt es, dass zuerst alle Instanzen und dann nur 10 letzte Instanzen aufgenommen werden? Gibt es eine effektivere Methode?

krzyhub
quelle

Antworten:

304

Django-Abfragesätze sind faul. Das bedeutet, dass eine Abfrage nur dann in die Datenbank gelangt, wenn Sie speziell nach dem Ergebnis fragen.

Bis Sie das Ergebnis einer Abfrage drucken oder tatsächlich verwenden, können Sie ohne Datenbankzugriff weiter filtern.

Wie Sie unten sehen können, führt Ihr Code nur eine SQL-Abfrage aus, um nur die letzten 10 Elemente abzurufen.

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>]
hamdiakoguz
quelle
Ich habe dies auf mongoDB versucht und es heißt, SELECT wird nicht unterstützt. Wie mache ich das auf mongoDB?
Winux
@winux Da dies Django-spezifisch ist, müssen Sie möglicherweise Django einrichten, um speziell mit Mongo / NoSQL-Datenbanken zu arbeiten. Dies ist meiner Erfahrung nach kein typisches Setup in Bezug auf das Standard-Django-ORM-Setup.
anonymer Feigling
38

Eigentlich denke ich, dass das LIMIT 10an die Datenbank ausgegeben wird, so dass das Schneiden nicht in Python, sondern in der Datenbank stattfinden würde.

Weitere Informationen finden Sie unter Einschränken von Abfragesätzen .

Davor Lucic
quelle
Beachten Sie, dass dies nicht für Abfragesätze funktioniert, die ebenfalls gefiltert werden müssen, da Sie nach dem Schneiden nicht filtern können.
Mike 'Pomax' Kamermans
2
Filtern Sie also zuerst, bevor Sie es in Scheiben schneiden. Danke Davor für den Link!
Vyachez
13

Es sieht so aus, als ob die Lösung in der Frage nicht mehr mit Django 1.7 funktioniert und einen Fehler auslöst: "Eine Abfrage kann nicht neu angeordnet werden, sobald ein Slice erstellt wurde."

Gemäß der Dokumentation https://docs.djangoproject.com/de/dev/topics/db/queries/#limiting-querysets wertet das Erzwingen des "step" -Parameters der Python-Slice-Syntax die Abfrage aus. Es funktioniert so:

Model.objects.all().order_by('-id')[:10:1]

Trotzdem frage ich mich, ob das Limit in SQL- oder Python-Slices ausgeführt wird und das gesamte Ergebnisarray zurückgegeben wird. Es ist nicht gut, große Listen in den Anwendungsspeicher abzurufen.

Nikolay Grischenko
quelle
Auch diese Lösung funktioniert nicht mit Django> = 1.8 getestet.
Sonus21
3

Ja. Wenn Sie eine begrenzte Teilmenge von Objekten abrufen möchten, können Sie dies mit dem folgenden Code tun:

Beispiel:

obj=emp.objects.all()[0:10]

Der Anfang 0 ist also optional

obj=emp.objects.all()[:10]

Der obige Code gibt die ersten 10 Instanzen zurück.

Patel Shahrukh
quelle
1

Als Ergänzung und Beobachtung zu den anderen nützlichen Antworten ist zu beachten, dass beim tatsächlichen [:10]Schneiden die ersten 10 Elemente der Liste zurückgegeben werden , nicht die letzten 10 ...

Um die letzten 10 zu erhalten, sollten Sie [-10:]stattdessen (siehe hier ) tun . Auf diese Weise können Sie vermeiden, die Elemente order_by('-id')mit -umzukehren.

DarkCygnus
quelle
1
Ich habe dies versucht und bekam "Negative Indizierung wird nicht unterstützt."
bparker
@DarkCygnus Product.objects.filter(~Q(price=0))[-5:]verursacht mir den gleichen Fehler: "Negative Indizierung wird nicht unterstützt."
Bersam
Dies funktioniert in Django auf einem Abfragesatz nicht: code.djangoproject.com/ticket/13089 Wenn Sie den Abfragesatz in eine Liste konvertieren, funktioniert er.
Valem