Django - ограничение результатов запроса


200

Я хочу взять последние 10 экземпляров модели и иметь этот код:

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

Правда ли, что сначала подберут все экземпляры, а затем возьмут только 10 последних? Есть ли более эффективный метод?


Ответы:


304

Наборы запросов Django ленивы. Это означает, что запрос попадет в базу данных только тогда, когда вы конкретно запросите результат.

Таким образом, до тех пор, пока вы не распечатаете или не используете результат запроса, вы можете выполнять дальнейшую фильтрацию без доступа к базе данных.

Как вы можете видеть ниже, ваш код выполняет только один 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, а в базе данных.

См. Limiting-querysets для получения дополнительной информации.


Обратите внимание, что это не будет работать для наборов запросов, которые также нуждаются в фильтрации, так как вы не можете фильтровать после нарезки.
Майк 'Pomax' Камерманс

2
Поэтому сначала отфильтруйте, чем нарежьте. Спасибо Давору за ссылку!
Вячес

13

Похоже, что решение в вопросе больше не работает с Django 1.7 и выдает ошибку: «Невозможно изменить порядок запроса после того, как был взят фрагмент»

Согласно документации https://docs.djangoproject.com/en/dev/topics/db/queries/#limiting-querysets, принудительное использование параметра «step» синтаксиса фрагмента 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 Если вы преобразуете набор запросов в список, он будет работать.
Валем
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.