Django - limitando resultados da consulta

200

Eu quero pegar as últimas 10 instâncias de um modelo e ter este código:

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

É verdade que, em primeiro lugar, todas as instâncias são capturadas e depois são coletadas apenas as dez últimas? Existe algum método mais eficaz?

krzyhub
fonte

Respostas:

304

Os querysets do Django são preguiçosos. Isso significa que uma consulta atingirá o banco de dados somente quando você solicitar especificamente o resultado.

Portanto, até que você imprima ou use realmente o resultado de uma consulta, poderá filtrar ainda mais sem acesso ao banco de dados.

Como você pode ver abaixo, seu código executa apenas uma consulta sql para buscar apenas os últimos 10 itens.

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
fonte
Eu tentei isso no mongoDB e diz SELECT não suportado. Como fazer isso no mongoDB?
winux 2/01/19
@winux Como isso é específico do Django, parece que você pode precisar configurar o Django para trabalhar especificamente com bancos de dados do tipo Mongo / NoSQL. Essa não é uma configuração típica em minha experiência, com relação à configuração padrão do Django ORM.
covarde anônimo
38

Na verdade, acho que o LIMIT 10seria emitido para o banco de dados, para que o fatiamento não ocorresse no Python, mas no banco de dados.

Veja limit-querysets para mais informações.

Davor Lucic
fonte
Observe que isso não funcionará para conjuntos de consultas que também precisam de filtragem, pois você não pode filtrar após o fatiamento.
Mike 'Pomax' Kamermans
2
Portanto, filtre primeiro que cortá-lo. Obrigado Davor pelo link!
Vyachez 26/07/19
13

Parece que a solução na pergunta não funciona mais com o Django 1.7 e gera um erro: "Não é possível reordenar uma consulta depois que uma fatia foi feita"

De acordo com a documentação https://docs.djangoproject.com/en/dev/topics/db/queries/#limiting-querysets forçando o parâmetro "step" da sintaxe de fatia do Python, avalia a consulta. Funciona assim:

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

Ainda me pergunto se o limite é executado em fatias SQL ou Python, toda a matriz de resultados retornada. Não é bom recuperar listas enormes para a memória do aplicativo.

Nikolay Grischenko
fonte
Mesmo esta solução não funciona com django> = 1.8 testado.
sonus21
3

Sim. Se você deseja buscar um subconjunto limitado de objetos, é possível com o código abaixo:

Exemplo:

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

O começo 0 é opcional, então

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

O código acima retorna as 10 primeiras instâncias.

Patel Shahrukh
fonte
1

Como uma adição e observação para as outras respostas úteis, vale a pena notar que, na verdade, fazer [:10]o fatiamento retornará os 10 primeiros elementos da lista , não os últimos 10 ...

Para obter os últimos 10, você deve fazê-lo [-10:](veja aqui ). Isso ajudará a evitar o uso order_by('-id')com o -para reverter os elementos.

DarkCygnus
fonte
1
Eu tentei isso e obtive "A indexação negativa não é suportada".
bparker
@DarkCygnus Product.objects.filter(~Q(price=0))[-5:]me causa o mesmo erro: "A indexação negativa não é suportada."
bersam
Isso não funciona no django em um conjunto de consultas : code.djangoproject.com/ticket/13089 Se você converter o conjunto de consultas em uma lista, ele funcionará.
valem 9/01