Django - обмеження результатів запиту


200

Я хочу взяти останні 10 екземплярів моделі і мати цей код:

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

Це правда, що спочатку підберіть всі екземпляри, а потім візьміть лише 10 останніх? Чи є якийсь більш ефективний метод?


Відповіді:


304

Запити Джанго ледачі. Це означає, що запит потрапить у базу даних лише тоді, коли ви конкретно запитаєте результат.

Отже, поки ви не надрукуєте або фактично не використаєте результат запиту, ви можете додатково фільтрувати без доступу до бази даних.

Як ви бачите нижче, ваш код виконує лише один запит sql, щоб отримати лише останні 10 елементів.

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>]

Я спробував це на mongoDB, і він говорить, що SELECT не підтримується. Як це зробити на mongoDB?
winux

@winux Оскільки це специфічно для Django, схоже, вам може знадобитися вивчити налаштування Django для роботи спеціально з базами даних Mongo / NoSQL. Це не типова установка в моєму досвіді, що стосується стандартної установки Django ORM.
анонімний трус

38

Насправді я думаю, що LIMIT 10це буде видано в базу даних, тому розрізання відбуватиметься не в Python, а в базі даних.

Додаткову інформацію див. У наборах обмежувальних запитів .


Зауважте, що це не буде працювати для наборів запитів, які також потребують фільтрації, оскільки ви не можете фільтрувати після нарізки.
Майк 'Помакс' Камерманс

2
Тому фільтруйте спочатку, ніж наріжте його. Дякую Давору за посилання!
В'ячез

13

Схоже, рішення в питанні більше не працює з Django 1.7, і виникає помилка: "Неможливо впорядкувати запит, коли фрагмент був зроблений"

Відповідно до документації https://docs.djangoproject.com/en/dev/topics/db/queries/#limiting-querysets, що примушує параметр "крок" синтаксису зрізу Python, оцінює Запит. Це працює так:

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

Ще мені цікаво, чи виконується ліміт у SQL або Python-фрагментах, весь масив результатів, що повертається. Немає користі для отримання величезних списків до пам'яті додатків.


Навіть це рішення не працює з django> = 1.8 перевіреним.
sonus21

3

Так. Якщо ви хочете отримати обмежений набір об'єктів, ви можете скористатися наведеним нижче кодом:

Приклад:

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

Початок 0 необов’язковий, значить

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

Вищевказаний код повертає перші 10 екземплярів.


1

Як доповнення та зауваження до інших корисних відповідей, варто зауважити, що насправді виконання [:10]нарізки поверне перші 10 елементів списку , а не останні 10 ...

Щоб отримати останні 10, вам слід зробити це [-10:]замість цього (див. Тут ). Це допоможе вам уникнути використання order_by('-id')з -повністю змінити елементи.


1
Я спробував це і отримав "Негативне індексування не підтримується".
bparker

@DarkCygnus Product.objects.filter(~Q(price=0))[-5:]викликає у мене таку ж помилку: "Негативна індексація не підтримується."
Берсам

Це не працює у django на наборі запитів: code.djangoproject.com/ticket/13089 Якщо перетворити набір запитів у список, він буде працювати.
valem
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.