Verificando o conjunto de consultas vazio no Django

183

Qual é o idioma recomendado para verificar se uma consulta retornou algum resultado?
Exemplo:

orgs = Organisation.objects.filter(name__iexact = 'Fjuk inc')
# If any results
    # Do this with the results without querying again.
# Else, do something else...

Suponho que existem várias maneiras diferentes de verificar isso, mas eu gostaria de saber como um usuário experiente do Django faria isso. A maioria dos exemplos nos documentos ignora o caso em que nada foi encontrado ...

Niklas
fonte

Respostas:

205
if not orgs:
    # Do this...
else:
    # Do that...
Adão
fonte
5
Isso também parece ser preferido na documentação, por exemplo: docs.djangoproject.com/en/1.8/topics/http/shortcuts/#id7
Wtower
1
@Wtower O código ao qual você se refere tem contrato para aumentar 404 se a expressão de filtragem não atingir nenhum registro ou produzir um listresultado, se houver registros. O código atingirá o banco de dados apenas uma vez. Se eles usassem exist()ou count()primeiro verificassem se haveriam registros retornados, estariam acessando o banco de dados duas vezes (uma vez para verificar, uma vez para obter os registros). Esta é uma situação específica. Não implica que, no caso geral , o método preferido para saber se a consulta irá retornar registros é para uso nãoif queryset:...
Louis
1
@ Louis, o código a que me refiro é apenas um exemplo que contém uma linha if not my_objects:para demonstrar que é assim que eles fazem nos documentos. Tudo o mais é totalmente irrelevante, por isso não entendo seu argumento. Eles poderiam fazer mil perguntas e ainda assim seria totalmente irrelevante, pois esse não é o objetivo desta resposta, com a qual deixo claro que concordo.
Wtower 04/11
1
@Wtower Essa é apenas uma explicação de como get_object_or_404funciona, não uma maneira preferida de verificar se existe algum elemento em um conjunto de consultas. Fazer list () em um conjunto de consultas buscará todos os objetos em um conjunto de consultas, o que seria pior do que consultar duas vezes se houver muitas linhas retornadas.
Min
1
Para uma resposta mais detalhada, veja a resposta de @ leonid-shvechikov abaixo: o uso .exists()é mais eficiente se o qs não for avaliado.
guival
191

Desde a versão 1.2, o Django possui o QuerySet. Existe o método () que é o mais eficiente:

if orgs.exists():
    # Do this...
else:
    # Do that...

Mas se você avaliar o QuerySet de qualquer maneira, é melhor usar:

if orgs:
   ...

Para mais informações, leia a documentação QuerySet.exists () .

Leonid Shvechikov
fonte
.exists () é apenas para .filter (), existe algo para .get ()?
rolo de
.getnão retorna um conjunto de consultas. Retorna um objeto. Portanto, pesquise no google
Aseem
É apenas visivelmente mais eficiente se você tem um grande QuerySet: docs.djangoproject.com/en/2.1/ref/models/querysets/#exists
Nathan Jones
16

Se você possui um grande número de objetos, isso pode (às vezes) ser muito mais rápido:

try:
    orgs[0]
    # If you get here, it exists...
except IndexError:
    # Doesn't exist!

Em um projeto em que estou trabalhando com um banco de dados enorme, not orgstem mais de 400 ms e orgs.count()250ms. Nos meus casos de uso mais comuns (aqueles em que existem resultados), essa técnica geralmente reduz isso para menos de 20ms. (Um caso que encontrei foi o 6.)

É possível que demore muito mais, dependendo da distância que o banco de dados deve procurar para encontrar um resultado. Ou ainda mais rápido, se encontrar um rapidamente; YMMV.

EDIT: Este vai muitas vezes ser mais lento do que orgs.count()se o resultado não for encontrado, especialmente se a condição que você está filtrando é rara; como resultado, é particularmente útil nas funções de exibição nas quais você precisa garantir que a exibição exista ou lançar o Http404. (Onde, seria de esperar, as pessoas estão pedindo URLs que existem com mais frequência do que não).

Adam Playford
fonte
10

Para verificar o vazio de um conjunto de consultas:

if orgs.exists():
    # Do something

ou você pode procurar o primeiro item em um conjunto de consultas, se ele não existir, retornará None:

if orgs.first():
    # Do something
Tuss4
fonte
7
if orgs.exists()foi coberto por uma resposta que foi fornecida cerca de 5 anos antes desta. A única coisa que essa resposta traz para a mesa que talvez seja nova é if orgs.first(). (Mesmo isso é discutível: é substancialmente diferente de fazer o orgs[0] sugerido há cerca de 5 anos também?) Você deveria desenvolver essa parte da resposta: quando alguém iria querer fazer isso em vez das outras soluções propostas anteriormente?
Louis
9

A maneira mais eficiente (antes do django 1.2) é esta:

if orgs.count() == 0:
    # no results
else:
    # alrigh! let's continue...
Bartosz
fonte
5
.exists () parece ser ainda mais eficiente
Dzida
5
Exceto que .exists () foi adicionado alguns meses após o meu comentário e o Django 1.2 (que incorporou essa API) foi lançado ~ 8 meses depois. Mas obrigado pela baixa votação e não se preocupe em verificar os fatos.
Bartosz
4
Desculpe, adicionei uma edição pequena à sua resposta para torná-la mais precisa e com um voto positivo.
Dzida
4

Eu discordo do predicado

if not orgs:

Deveria ser

if not orgs.count():

Eu estava tendo o mesmo problema com um conjunto de resultados bastante grande (~ 150k resultados). O operador não está sobrecarregado no QuerySet; portanto, o resultado é realmente descompactado como uma lista antes da verificação. No meu caso, o tempo de execução diminuiu três ordens.

hedleyroos
fonte
6
__nonzero__ já está sobrecarregado no QuerySet. Se o resultado não for armazenado em cache (ele nunca será usado pela primeira vez no conjunto de consultas), o comportamento de __nonzero__ será iterar sobre todos os elementos no conjunto de consultas. Isso é muito ruim se o conjunto for grande.
Hedleyroos
0

Você também pode usar isso:

if(not(orgs)): #if orgs is empty else: #if orgs is not empty

Rupesh Chaudhari
fonte