Jak zrobić nierównomierne filtrowanie zestawu zapytań Django?

664

W modelu QuerySets Django widzę, że istnieją wartości porównawcze __gti __ltdla nich, ale czy istnieje __ne/ !=/ <>( nie równa się ?)

Chcę odfiltrować za pomocą nie równej:

Przykład:

Model:
    bool a;
    int x;

chcę

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

!=Nie jest prawidłowa składnia. Próbowałem __ne, <>.

Skończyło się na tym, że:

results = Model.objects.exclude(a=true, x__lt=5).exclude(a=true, x__gt=5)
MikeN
źródło
75
Czy wyniki = Model.objects.exclude (a = true). Filter (x = 5) działałyby?
hughdbrown
3
@hughdbrown. Nie. Twoje zapytanie a=truenajpierw wyklucza wszystkie, a następnie stosuje x=5filtr do pozostałych. Zamierzone zapytanie wymagało tylko tych z a=truei x!=5. Różnica polega na tym, że wszystkie te z a=truei x=5są również odfiltrowane.
Mitchell van Zuylen

Odpowiedzi:

689

Może obiekty Q mogłyby pomóc w rozwiązaniu tego problemu. Nigdy ich nie użyłem, ale wygląda na to, że można je zanegować i połączyć podobnie jak normalne wyrażenia w języku python.

Aktualizacja: Właśnie go wypróbowałem, wydaje się działać całkiem dobrze:

>>> 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
źródło
16
@ JCLeitão: zobacz także odpowiedź @ d4nt poniżej, aby uzyskać bardziej intuicyjną składnię.
Paul D. Waite,
610

Twoje zapytanie wydaje się mieć podwójną wartość ujemną, chcesz wykluczyć wszystkie wiersze, w których x nie wynosi 5, więc innymi słowy, chcesz uwzględnić wszystkie wiersze, w których x IS 5. Wierzę, że to załatwi sprawę.

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

Aby odpowiedzieć na twoje konkretne pytanie, nie ma „nie równego”, ale prawdopodobnie dlatego, że django ma zarówno dostępne metody „filtrowania”, jak i „wykluczania”, więc zawsze możesz po prostu zmienić logikę, aby uzyskać pożądany wynik.

d4nt
źródło
2
@ d4nt: Mogę się mylić, ale myślę, że zapytanie powinno byćresults = Model.objects.filter(a=true).exclude(x=5)
Taranjeet
1
@Taranjeet: Myślę, że źle odczytałeś oryginalne zapytanie. Wersja d4nt jest poprawna, ponieważ OP chciał wykluczyć (a = Prawda) i negować wykluczenie x = 5 (tj. dołączyć).
Chuck,
3
Myślę, że to źle, ponieważ instancja (x = 4, a = fałsz) zostałaby błędnie wykluczona.
RemcoGerlich,
4
@danigosa To nie wydaje się właściwe. Właśnie próbowałem tego sam, a kolejność excludei filterpołączenia nie miały znaczącej różnicy. Kolejność warunków w WHEREklauzuli zmienia się, ale jak to ma znaczenie?
coredumperror
4
@danigosa kolejność wykluczania i filtrowania nie ma znaczenia.
EralpB,
132

field=valueskładnia zapytań jest skrótem field__exact=value. To znaczy, że Django umieszcza operatory zapytania na polach zapytania w identyfikatorach . Django obsługuje następujące operatory:

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

Jestem pewien, że łącząc je z obiektami Q, jak sugeruje Dave Vogt, i używając filter()lub exclude()jak sugeruje Jason Baker, dostaniesz dokładnie to, czego potrzebujesz na każde możliwe zapytanie.

SingleNegationElimination
źródło
dzięki, to jest niesamowite. użyłem czegoś takiego tg=Tag.objects.filter(user=request.user).exclude(name__regex=r'^(public|url)$')i to działa.
suhailvs,
@suhail, pamiętaj, że nie wszystkie bazy danych obsługują składnię
wyrażeń
2
i in icontains, iexacti podobne oznaczają „ignoruj ​​rozróżnianie wielkości liter”. Nie dotyczy „odwrotności”.
Ivy Growing
Warto zauważyć, że jeśli używasz exclude()wielu terminów, możesz chcieć skomponować propozycję z ORoperatorem, np. exclude(Q(field1__queryop1=value1) | Q(field2__queryop2=value2))W celu wykluczenia wyników w obu warunkach.
clapas,
98

Łatwo jest utworzyć niestandardowe wyszukiwanie za pomocą Django 1.7. __nePrzykładowy odnośnik znajduje się w oficjalnej dokumentacji Django .

Najpierw musisz utworzyć sam odnośnik:

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

Następnie musisz go zarejestrować:

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

A teraz możesz użyć __neodnośnika w swoich zapytaniach w następujący sposób:

results = Model.objects.exclude(a=True, x__ne=5)
Dmitrii Mikhailov
źródło
88

W Django 1.9 / 1.10 istnieją trzy opcje.

  1. Łańcuch excludeifilter

    results = Model.objects.exclude(a=true).filter(x=5)
  2. Używaj Q()obiektów i ~operatora

    from django.db.models import Q
    object_list = QuerySet.filter(~Q(a=True), x=5)
  3. Zarejestruj niestandardową funkcję wyszukiwania

    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

    register_lookupDekorator dodano Django 1,8 i pozwala na zamówienie odnośnika, jak zwykle:

    results = Model.objects.exclude(a=True, x__ne=5)
ilse2005
źródło
1
object_list = QuerySet.filter (~ Q (a = True), x = 5): Pamiętaj, aby zachować wszystkie pozostałe warunki niezawierające Q po tych zawierających Q.
Bhumi Singhal
1
@MichaelHoffmann: A) będziesz następnie filtrować mniejszy zestaw danych po wykluczeniu za pomocą ~ Q, więc jest bardziej wydajny. B) prawdopodobnie sekwencjonowanie na odwrót nie działa .. nie wiem ... nie pamiętaj!
Bhumi Singhal
41

Choć z modelami można filtrować z =, __gt, __gte, __lt, __lte, nie można używać ne, !=lub<> . Można jednak uzyskać lepsze filtrowanie przy użyciu obiektu Q.

Można uniknąć łączenia QuerySet.filter()i QuerySet.exlude(), i użyj tego:

from django.db.models import Q
object_list = QuerySet.filter(~Q(field='not wanted'), field='wanted')
Dami
źródło
24

Do czasu decyzji projektowej. Tymczasem użyjexclude()

Moduł śledzenia problemów Django ma niezwykły wpis # 5763 , zatytułowany „Zestaw zapytań nie ma operatora filtru„ nie równy ”” . Jest to niezwykłe, ponieważ (od kwietnia 2016 r.) Został „otwarty 9 lat temu” (w epoce kamienia Django), „zamknięty 4 lata temu” i „ostatnio zmieniony 5 miesięcy temu”.

Przeczytaj dyskusję, to jest interesujące. Zasadniczo niektórzy twierdzą, że __nenależy dodać, podczas gdy inni twierdzą, że exclude()jest jaśniejszy, a zatem nie__ne powinien go dodawać.

(Zgadzam się z pierwszym, ponieważ drugi argument jest mniej więcej równoważny z twierdzeniem, że Python nie powinien mieć, !=ponieważ ma ==i notjuż ...)

Lutz Prechelt
źródło
22

Używanie wykluczania i filtrowania

results = Model.objects.filter(x=5).exclude(a=true)
jincy mariam
źródło
18

Powinieneś użyć filteri excludetak

results = Model.objects.exclude(a=true).filter(x=5)
brak czasu
źródło
8

Ostatni bit kodu wyklucza wszystkie obiekty, w których x! = 5, a a ma wartość True. Spróbuj tego:

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

Pamiętaj, że znak = w powyższej linii przypisuje wartość Fałsz do parametru a, a liczbę 5 do parametru x. Nie sprawdza równości. Dlatego tak naprawdę nie ma żadnego sposobu użycia symbolu! = W wywołaniu zapytania.

Jason Baker
źródło
3
To nie jest w 100% to samo, ponieważ dla tych pól mogą być również wartości Null.
MikeN
Zwraca to tylko te elementy, które mają = Fałsz i x = 5, ale w pytaniu zostanie uwzględniona instancja (a = fałsz, x = 4).
RemcoGerlich,
1
results = Model.objects.filter(a__in=[False,None],x=5)
Jeremy
8

results = Model.objects.filter (a = True) .exclude (x = 5)
Generetes this sql:
wybierz * z tablex, gdzie a! = 0 i x! = 5
SQL zależy od tego, jak reprezentowane jest twoje pole Prawda / Fałsz oraz od silnika bazy danych. Jednak kod django jest wszystkim, czego potrzebujesz.

M. Dasn
źródło
8

Django-model-values (ujawnienie: autor) zapewnia implementację wyszukiwania NotEqual , jak w tej odpowiedzi . Zapewnia również obsługę składniową:

from model_values import F
Model.objects.exclude(F.x != 5, a=True)
A. Coady
źródło
6

Co szukasz są wszystkie obiekty, które mają albo a=false albo x=5 . W Django |służy jako ORoperator między zestawami zapytań:

results = Model.objects.filter(a=false)|Model.objects.filter(x=5)
Gerard
źródło
5

To da pożądany rezultat.

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

dla nierównego możesz użyć ~dla równego zapytania. oczywiście Qmożna go użyć do osiągnięcia równego zapytania.

Milad Khodabandehloo
źródło
Sprawdź edycję; użycie „i” w Q(a=True) and ~Q(x=5)spowoduje, że będą ~Q(x=5)argumentami .exclude. Proszę przeczytać: docs.python.org/3/reference/expressions.html#boolean-operations i docs.python.org/3/reference/… .
tzot
2

Uważaj na wiele niepoprawnych odpowiedzi na to pytanie!

Logika Gerarda jest poprawna, ale zwraca listę zamiast zestawu zapytań (co może nie mieć znaczenia).

Jeśli potrzebujesz zestawu zapytań, użyj Q:

from django.db.models import Q
results = Model.objects.filter(Q(a=false) | Q(x=5))
Mark Bailey
źródło