Filtrando por nomes vazios ou NULL em um conjunto de consultas

463

Eu tenho first_name, last_namee alias(opcional) que eu preciso procurar. Então, eu preciso de uma consulta para me fornecer todos os nomes que possuem um alias definido.

Somente se eu pudesse fazer:

Name.objects.filter(alias!="")

Então, qual é o equivalente ao acima?

un33k
fonte

Respostas:

840

Você pode fazer isso:

Name.objects.exclude(alias__isnull=True)

Se você precisar excluir valores nulos e cadeias vazias, a maneira preferida de fazer isso é encadear as condições da seguinte forma:

Name.objects.exclude(alias__isnull=True).exclude(alias__exact='')

O encadeamento desses métodos basicamente verifica cada condição independentemente: no exemplo acima, excluímos linhas onde aliasé nulo ou uma sequência vazia, para que você obtenha todos os Nameobjetos que possuem um campo não nulo e não vazio alias. O SQL gerado seria algo como:

SELECT * FROM Name WHERE alias IS NOT NULL AND alias != ""

Você também pode passar vários argumentos para uma única chamada exclude, o que garantiria que apenas os objetos que atendam a todas as condições sejam excluídos:

Name.objects.exclude(some_field=True, other_field=True)

Aqui, as linhas em que some_field e other_field true são excluídas, então obtemos todas as linhas em que ambos os campos não são verdadeiros. O código SQL gerado ficaria assim:

SELECT * FROM Name WHERE NOT (some_field = TRUE AND other_field = TRUE)

Como alternativa, se sua lógica é mais complexa que isso, você pode usar os objetos Q do Django :

from django.db.models import Q
Name.objects.exclude(Q(alias__isnull=True) | Q(alias__exact=''))

Para mais informações, veja esta página e esta página nos documentos do Django.

Como um aparte: Meus exemplos de SQL são apenas uma analogia - o código SQL real gerado provavelmente parecerá diferente. Você terá uma compreensão mais profunda de como as consultas do Django funcionam, na verdade, olhando para o SQL que elas geram.

Sasha Chedygov
fonte
5
Acredito que sua edição esteja incorreta: o filtro de encadeamento NÃO cria automaticamente um SQL OR(somente neste caso), ele produz um SQL AND. Consulte esta página para referência: docs.djangoproject.com/en/dev/topics/db/queries/… A vantagem do encadeamento é que você pode misturar excludee filtermodelar condições de consulta complicadas. Se você deseja modelar um SQL real, ORvocê deve usar um objeto Django Q: docs.djangoproject.com/en/dev/topics/db/queries/… Edite sua edição para refletir isso, pois a resposta é muito enganosa do jeito que está .
Shezi
1
@shezi: Eu quis dizer isso mais como uma analogia - não quis dizer que o código SQL real é garantido para usar um ORpara fundir as condições. Vou editar minha resposta para esclarecer.
Sasha Chedygov
1
Lembre-se de que existem maneiras diferentes de representar essa lógica - por exemplo, NOT (A AND B)é equivalente a NOT A OR NOT B. Eu acho que isso torna as coisas confusas para os novos desenvolvedores do Django que conhecem SQL, mas não estão familiarizados com ORMs.
Sasha Chedygov
3
Conheço a lei de De Morgan, e esse é exatamente o meu ponto: seu exemplo só funciona porque é vantajoso transformar ANDa primeira consulta em uma ORporque você está usando exclude. No caso geral, provavelmente é mais correto pensar em encadeamento como um THEN, ie exclude(A) THEN exclude(B). Desculpe pela linguagem severa acima. Sua resposta é realmente boa, mas estou preocupado com os novos desenvolvedores levarem sua resposta muito geralmente.
Shezi
2
@shezi: Justo. Concordo que é melhor pensar nisso em termos do Django e não em SQL, apenas pensei que apresentar encadeamento em termos de ANDe ORpode ser útil para alguém que vem ao Django com experiência em SQL. Para uma compreensão mais profunda do Django, acho que os documentos fazem um trabalho melhor do que eu.
Sasha Chedygov 13/02/2013
49
Name.objects.filter(alias__gt='',alias__isnull=False)
jbofill
fonte
2
Não tenho certeza, mas acho que a alias__isnull=Falsecondição é redundante. Se o campo for Nullcertamente, será excluído pela primeira cláusula?
quer
Além do meu comentário / pergunta anterior, acho que a lógica positiva aqui é mais fácil de seguir do que em algumas das outras respostas.
quer
@Bobble que dependem da implementação de banco de dados - ordenação é delegada ao databse
wpercy
alias__gtfoi a única coisa que funcionou para colunas do tipo JSON em que eu queria excluir cadeias de caracteres vazias do JSON {'something:''}. Portanto, a sintaxe do trabalho é:jsoncolumnname__something__gt=''
bartgras 30/03
38

Primeiramente, os documentos do Django recomendam enfaticamente não usar valores NULL para campos baseados em string, como CharField ou TextField. Leia a documentação para a explicação:

https://docs.djangoproject.com/en/dev/ref/models/fields/#null

Solução: Você também pode encadear métodos no QuerySets, eu acho. Tente o seguinte:

Name.objects.exclude(alias__isnull=True).exclude(alias="")

Isso deve lhe dar o conjunto que você está procurando.

b3ng0
fonte
5

Do Django 1.8,

from django.db.models.functions import Length

Name.objects.annotate(alias_length=Length('alias')).filter(alias_length__gt=0)
Programador químico
fonte
5
Parece um "algo que você pode fazer", não algo que você deve fazer. Ele aumenta significativamente a complexidade da consulta em duas verificações simples.
Oli
3

Para evitar erros comuns ao usar exclude, lembre-se:

Você pode não adicionar várias condições em um bloco exclude () como filter. Para excluir várias condições, você deve usar várias exclusões ()

Exemplo

Incorreto :

User.objects.filter (email='[email protected] '). Exclude (profile__nick_name =' ', profile__avt =' ')

Correto :

User.objects.filter (email='[email protected] '). Exclude (profile__nick_name =' '). Exclude (profile__avt =' ')

HoangYell
fonte
0

Você pode simplesmente fazer isso:

Name.objects.exclude(alias="").exclude(alias=None)

É realmente simples assim. filteré usado para corresponder e excludecorresponde a tudo, exceto o que ele especifica. Isso avaliaria em SQL como NOT alias='' AND alias IS NOT NULL.

Tim Tisdall
fonte
Isto está incorreto. A pergunta tem como objetivo excluir aliases vazios ( alias="") e NULL ( alias=None) da consulta. O seu incluiria instâncias com Name(alias=None).
Damon
@damon - eu estava respondendo o que era equivalente, .filter(alias!="")mas não o título. Eu editei minha resposta. No entanto, os campos de caracteres não devem permitir valores NULL e usar a sequência vazia para um valor não (conforme a convenção).
precisa
-1

essa é outra maneira simples de fazer isso.

Name.objects.exclude(alias=None)
ImadOS
fonte
Nonenão é a mesma coisa que "".
Tim Tisdall