Django - membatasi hasil kueri

200

Saya ingin mengambil 10 contoh terakhir dari model dan memiliki kode ini:

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

Benarkah yang pertama mengambil semua contoh, dan kemudian hanya mengambil 10 yang terakhir? Apakah ada metode yang lebih efektif?

krzyhub
sumber

Jawaban:

304

Kuartet Django malas. Itu berarti permintaan akan mengenai database hanya ketika Anda secara spesifik meminta hasilnya.

Jadi sampai Anda mencetak atau benar-benar menggunakan hasil kueri, Anda dapat memfilter lebih jauh tanpa akses database.

Seperti yang Anda lihat di bawah, kode Anda hanya menjalankan satu kueri sql untuk mengambil hanya 10 item terakhir.

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
sumber
Saya mencoba ini pada mongoDB dan dikatakan SELECT tidak didukung. Bagaimana melakukan ini di mongoDB?
winux
@ winux Karena ini adalah Django khusus, sepertinya Anda mungkin perlu melihat ke pengaturan Django untuk bekerja secara khusus dengan database tipe Mongo / NoSQL. Itu bukan pengaturan khas dalam pengalaman saya, sehubungan dengan pengaturan ORM Django standar.
pengecut anonim
38

Sebenarnya saya pikir LIMIT 10akan dikeluarkan ke basis data sehingga mengiris tidak akan terjadi di Python tetapi dalam database.

Lihat limiting-querysets untuk informasi lebih lanjut.

Davor Lucic
sumber
Perhatikan bahwa ini tidak akan berfungsi untuk kueri yang juga perlu difilter, karena Anda tidak dapat memfilter setelah mengiris.
Mike 'Pomax' Kamermans
2
Jadi saring dulu daripada mengirisnya. Terima kasih Davor untuk tautan!
Vyachez
13

Sepertinya solusi dalam pertanyaan tidak lagi berfungsi dengan Django 1.7 dan menimbulkan kesalahan: "Tidak dapat memesan ulang permintaan setelah potongan diambil"

Menurut dokumentasi https://docs.djangoproject.com/en/dev/topics/db/queries/#limiting-querysets yang memaksa parameter “step” dari sintaks iris slice Python mengevaluasi Query. Cara kerjanya seperti ini:

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

Masih saya bertanya-tanya apakah batas dijalankan dalam SQL atau Python iris seluruh hasil array dikembalikan. Tidak ada gunanya mengambil daftar besar ke memori aplikasi.

Nikolay Grischenko
sumber
Bahkan solusi ini tidak bekerja dengan Django> = 1,8 diuji.
sonus21
3

Iya. Jika Anda ingin mengambil subset objek terbatas, Anda bisa dengan kode di bawah ini:

Contoh:

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

Awal 0 adalah opsional, jadi

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

Kode di atas mengembalikan 10 instance pertama.

patel shahrukh
sumber
1

Sebagai tambahan dan pengamatan terhadap jawaban-jawaban lain yang bermanfaat, perlu diperhatikan bahwa melakukan [:10]slicing akan mengembalikan 10 elemen pertama dari daftar , bukan 10 yang terakhir ...

Untuk mendapatkan 10 terakhir yang harus Anda lakukan [-10:]sebagai gantinya (lihat di sini ). Ini akan membantu Anda menghindari penggunaan order_by('-id')dengan -untuk membalikkan elemen.

DarkCygnus
sumber
1
Saya mencoba ini dan mendapat "Pengindeksan negatif tidak didukung."
bparker
@DarkCygnus Product.objects.filter(~Q(price=0))[-5:]menyebabkan saya kesalahan yang sama: "Pengindeksan negatif tidak didukung."
bersam
Ini tidak berfungsi di Django pada queryset: code.djangoproject.com/ticket/13089 Jika Anda mengonversi queryset ke daftar, ia akan berfungsi.
valem