Como faço para não ser igual na filtragem do Django queryset?

666

No modelo do Django, QuerySets, vejo que há a __gte __ltpara valores comparativos, mas existe um __ne/ !=/ <>( não é igual a ?)

Quero filtrar usando um não é igual a:

Exemplo:

Model:
    bool a;
    int x;

eu quero

results = Model.objects.exclude(a=true, x!=5)

A !=sintaxe não está correta. Eu tentei __ne, <>.

Acabei usando:

results = Model.objects.exclude(a=true, x__lt=5).exclude(a=true, x__gt=5)
MikeN
fonte
75
Results = Model.objects.exclude (a = true) .filter (x = 5) teria funcionado?
27909 hughdbrown
3
@hughdbrown. Não. Sua consulta exclui tudo a=trueprimeiro e depois aplica o x=5filtro ao restante. A consulta pretendida requer apenas aqueles com a=truee x!=5. A diferença é que todos aqueles com a=truee x=5também são filtrados.
Mitchell van Zuylen

Respostas:

690

Talvez objetos Q possam ser úteis para esse problema. Eu nunca os usei, mas parece que eles podem ser negados e combinados como expressões python normais.

Atualização: Acabei de experimentar, parece funcionar muito bem:

>>> from myapp.models import Entry
>>> from django.db.models import Q

>>> Entry.objects.filter(~Q(id = 3))

[<Entry: Entry object>, <Entry: Entry object>, <Entry: Entry object>, ...]
Dave Vogt
fonte
16
@ JCLeitão: veja também a resposta de @ d4nt abaixo para obter uma sintaxe mais intuitiva.
Paul D. Waite
611

Sua consulta parece ter um duplo negativo; você deseja excluir todas as linhas em que x não é 5; portanto, em outras palavras, você deseja incluir todas as linhas em que x É 5. Acredito que isso funcionará.

results = Model.objects.filter(x=5).exclude(a=true)

Para responder sua pergunta específica, não existe um "não é igual a", mas provavelmente porque o django tem os métodos "filter" e "exclude" disponíveis para que você sempre possa alternar a lógica para obter o resultado desejado.

d4nt
fonte
2
@ d4nt: Posso estar errado, mas acho que a consulta deve serresults = Model.objects.filter(a=true).exclude(x=5)
Taranjeet
1
@ Taranjeet: Eu acho que você interpretou mal a consulta original. A versão do d4nt está correta, porque o OP queria excluir (a = True) e negar a exclusão de x = 5 (ou seja, incluí-la).
Chuck
3
Eu acho que isso está errado, porque uma instância (x = 4, a = false) seria erroneamente excluída.
RemoGerlich
4
@danigosa Isso não parece certo. Eu mesmo tentei isso, e a ordem excludee as filterchamadas não fizeram nenhuma diferença significativa. A ordem das condições na WHEREcláusula muda, mas como isso importa?
Coredumperror
4
@danigosa ordem de exclusão e filtro não importa.
EralpB
132

a field=valuesintaxe nas consultas é uma abreviação de field__exact=value. Isto é, o Django coloca operadores de consulta nos campos de consulta nos identificadores . O Django suporta os seguintes operadores:

exact
iexact
contains
icontains
in
gt
gte
lt
lte
startswith
istartswith
endswith
iendswith
range
year
month
day
week_day
isnull
search
regex
iregex

Tenho certeza de que, combinando-os com os objetos Q, como Dave Vogt sugere e usando filter()ou exclude()como Jason Baker sugere, você obterá exatamente o que precisa para praticamente qualquer consulta possível.

SingleNegationElimination
fonte
graças isso é incrível. Eu usei algo assim tg=Tag.objects.filter(user=request.user).exclude(name__regex=r'^(public|url)$')e funciona.
suhailvs
@suhail, entre em mente que nem todos os bancos de dados suportam que regex sintaxe :)
Anoyz
2
i in icontains, iexacte similar significa "ignorar maiúsculas e minúsculas". Não é para "inverso".
Ivy Growing
É importante notar que, quando você estiver usando exclude()vários termos, poderá compor a proposição com o ORoperador, por exemplo exclude(Q(field1__queryop1=value1) | Q(field2__queryop2=value2)), para excluir os resultados nas duas condições.
clapas
98

É fácil criar uma pesquisa personalizada com o Django 1.7. Há um __neexemplo de pesquisa na documentação oficial do Django .

Você precisa criar a própria pesquisa primeiro:

from django.db.models import Lookup

class NotEqual(Lookup):
    lookup_name = 'ne'

    def as_sql(self, qn, connection):
        lhs, lhs_params = self.process_lhs(qn, connection)
        rhs, rhs_params = self.process_rhs(qn, connection)
        params = lhs_params + rhs_params
        return '%s <> %s' % (lhs, rhs), params

Então você precisa registrá-lo:

from django.db.models.fields import Field
Field.register_lookup(NotEqual)

E agora você pode usar a __nepesquisa em suas consultas como esta:

results = Model.objects.exclude(a=True, x__ne=5)
Dmitrii Mikhailov
fonte
88

No Django 1.9 / 1.10, existem três opções.

  1. Cadeia excludeefilter

    results = Model.objects.exclude(a=true).filter(x=5)
  2. Use Q()objetos e o ~operador

    from django.db.models import Q
    object_list = QuerySet.filter(~Q(a=True), x=5)
  3. Registrar uma função de pesquisa personalizada

    from django.db.models import Lookup
    from django.db.models.fields import Field
    
    @Field.register_lookup
    class NotEqual(Lookup):
        lookup_name = 'ne'
    
        def as_sql(self, compiler, connection):
            lhs, lhs_params = self.process_lhs(compiler, connection)
            rhs, rhs_params = self.process_rhs(compiler, connection)
            params = lhs_params + rhs_params
            return '%s <> %s' % (lhs, rhs), params

    O register_lookupdecorador foi adicionado no Django 1.8 e permite a pesquisa personalizada como de costume:

    results = Model.objects.exclude(a=True, x__ne=5)
ilse2005
fonte
1
object_list = QuerySet.filter (~ Q (a = true), x = 5): Lembre-se de manter todas as outras condições que não contenham Q depois que contêm Q.
Bhumi Singhal
1
@ MichaelHoffmann: A) você filtrará um conjunto menor de dados após a exclusão usando ~ Q para que seja mais eficiente. B) provavelmente o seqüenciamento ao contrário não funciona .. sei lá .. lembre-se!
Bhumi Singhal
41

Enquanto com os modelos, você pode filtrar com =, __gt, __gte, __lt, __lte, você não pode usar ne, !=ou <>. No entanto, você pode obter uma melhor filtragem usando o objeto Q.

Você pode evitar o encadeamento QuerySet.filter()e QuerySet.exlude(), e usar isto:

from django.db.models import Q
object_list = QuerySet.filter(~Q(field='not wanted'), field='wanted')
Dami
fonte
24

Decisão de projeto pendente. Enquanto isso, useexclude()

O rastreador de problemas do Django possui a notável entrada # 5763 , intitulada "Queryset não possui um operador de filtro" diferente de "" . É notável porque (em abril de 2016) foi "aberto há 9 anos" (na idade da pedra do Django), "fechado há 4 anos" e "alterado pela última vez há 5 meses".

Leia a discussão, é interessante. Basicamente, algumas pessoas argumentam que __nedevem ser adicionadas, enquanto outras dizem que exclude()é mais claro e, portanto __ne , não devem ser adicionadas.

(Eu concordo com o anterior, porque o último argumento é mais ou menos equivalente a dizer Python não deve ter !=porque tem ==e notjá ...)

Lutz Prechelt
fonte
22

Usando excluir e filtrar

results = Model.objects.filter(x=5).exclude(a=true)
jincy mariam
fonte
18

Você deve usar filtere excludeassim

results = Model.objects.exclude(a=true).filter(x=5)
fora do tempo
fonte
8

O último bit de código excluirá todos os objetos em que x! = 5 e a é True. Tente o seguinte:

results = Model.objects.filter(a=False, x=5)

Lembre-se, o sinal = na linha acima está atribuindo False ao parâmetro ae o número 5 ao parâmetro x. Não está verificando a igualdade. Portanto, não há realmente nenhuma maneira de usar o símbolo! = Em uma chamada de consulta.

Jason Baker
fonte
3
Isso não é 100% a mesma coisa, pois também pode haver valores nulos para esses campos.
21710 MikeN
Isso retorna em somente os itens que têm um = false e x = 5, mas na questão de uma instância (a = false, x = 4) seria incluído.
RemcoGerlich
1
results = Model.objects.filter(a__in=[False,None],x=5)
Jeremy
8

resultados = Model.objects.filter (a = True) .exclude (x = 5)
Gera este sql:
selecione * da tabelax onde a! = 0 e x! = 5
O sql depende de como seu campo Verdadeiro / Falso é representado e do mecanismo de banco de dados. O código django é tudo o que você precisa.

M. Dasn
fonte
8

Django-model-values (disclosure: author) fornece uma implementação da pesquisa NotEqual , como nesta resposta . Ele também fornece suporte sintático para ele:

from model_values import F
Model.objects.exclude(F.x != 5, a=True)
A. Coady
fonte
6

O que você está procurando são todos os objetos que possuem a=false ou x=5 . No Django, |serve como ORoperador entre conjuntos de consultas:

results = Model.objects.filter(a=false)|Model.objects.filter(x=5)
Gerard
fonte
5

Isso dará o resultado desejado.

from django.db.models import Q
results = Model.objects.exclude(Q(a=True) & ~Q(x=5))

para não igualar você pode usar ~em uma consulta igual. obviamente, Qpode ser usado para alcançar a consulta igual.

Milad Khodabandehloo
fonte
Por favor, verifique a edição; usar "e" em Q(a=True) and ~Q(x=5)seria avaliado ~Q(x=5)como argumentos para .exclude. Leia: docs.python.org/3/reference/expressions.html#boolean-operations e docs.python.org/3/reference/… .
Tbot 11/10/19
2

Cuidado com muitas respostas incorretas para esta pergunta!

A lógica de Gerard está correta, embora retorne uma lista em vez de um conjunto de consultas (o que pode não importar).

Se você precisar de um conjunto de consultas, use Q:

from django.db.models import Q
results = Model.objects.filter(Q(a=false) | Q(x=5))
Mark Bailey
fonte