Wie mache ich eine ungleiche Django-Queryset-Filterung?

664

Im Django-Modell QuerySets sehe ich, dass es ein __gtund __ltfür Vergleichswerte gibt, aber gibt es ein __ne/ !=/ <>( nicht gleich ?)

Ich möchte mit einem ungleich herausfiltern:

Beispiel:

Model:
    bool a;
    int x;

ich möchte

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

Die !=Syntax ist nicht korrekt. Ich habe es versucht __ne, <>.

Am Ende habe ich verwendet:

results = Model.objects.exclude(a=true, x__lt=5).exclude(a=true, x__gt=5)
MikeN
quelle
75
Hätte results = Model.objects.exclude (a = true) .filter (x = 5) funktioniert?
Hughdbrown
3
@ Hughdbrown. Nein. Ihre Abfrage schließt a=truezuerst alle aus und wendet dann den x=5Filter auf die verbleibenden an. Die beabsichtigte Abfrage erforderte nur diejenigen mit a=trueund x!=5. Der Unterschied besteht darin, dass alle mit a=trueund x=5auch herausgefiltert werden.
Mitchell van Zuylen

Antworten:

688

Vielleicht könnten Q-Objekte bei diesem Problem hilfreich sein. Ich habe sie nie benutzt, aber es scheint, dass sie wie normale Python-Ausdrücke negiert und kombiniert werden können.

Update: Ich habe es gerade ausprobiert, es scheint ziemlich gut zu funktionieren:

>>> 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
quelle
16
@ JCLeitão: Eine intuitivere Syntax finden Sie auch in der Antwort von @ d4nt unten .
Paul D. Waite
609

Ihre Abfrage scheint doppelt negativ zu sein. Sie möchten alle Zeilen ausschließen, in denen x nicht 5 ist. Mit anderen Worten, Sie möchten alle Zeilen einschließen, in denen x 5 ist. Ich glaube, dies wird den Trick tun.

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

Um Ihre spezifische Frage zu beantworten, gibt es kein "ungleich", aber das liegt wahrscheinlich daran, dass in django sowohl "Filter" - als auch "Ausschluss" -Methoden verfügbar sind, sodass Sie jederzeit einfach die Logik umschalten können, um das gewünschte Ergebnis zu erzielen.

d4nt
quelle
2
@ d4nt: Ich kann mich irren, aber ich denke, die Abfrage sollte seinresults = Model.objects.filter(a=true).exclude(x=5)
Taranjeet
1
@ Taranjeet: Ich denke, Sie haben die ursprüngliche Abfrage falsch verstanden. Die Version von d4nt ist korrekt, da OP den Ausschluss von x = 5 ausschließen (a = True) und negieren wollte (dh ihn einschließen).
Chuck
3
Ich denke, das ist falsch, weil eine Instanz (x = 4, a = false) fälschlicherweise ausgeschlossen würde.
RemcoGerlich
4
@danigosa Das scheint nicht richtig. Ich habe es einfach selbst versucht, und die Reihenfolge excludeund die filterAnrufe machten keinen bedeutenden Unterschied. Die Reihenfolge der Bedingungen in der WHEREKlausel ändert sich, aber wie ist das wichtig?
Coredumperror
4
@danigosa Reihenfolge von Ausschluss und Filter spielt keine Rolle.
EralpB
132

Die field=valueSyntax in Abfragen ist eine Abkürzung für field__exact=value. Das heißt, Django setzt Abfrageoperatoren auf Abfragefelder in den Bezeichnern . Django unterstützt die folgenden Operatoren:

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

Ich bin sicher, indem ich diese mit den Q-Objekten als kombiniere Dave Vogt vorschlägt und verwendet, filter()oder exclude()wenn Jason Baker vorschlägt, erhalten Sie genau das, was Sie für nahezu jede mögliche Abfrage benötigen.

SingleNegationElimination
quelle
Danke, das ist großartig. Ich habe so etwas benutzt tg=Tag.objects.filter(user=request.user).exclude(name__regex=r'^(public|url)$')und es funktioniert.
Suhailvs
@suhail, bitte beachten Sie, dass nicht alle Datenbanken diese Regex-Syntax unterstützen :)
Anoyz
2
i in icontains, iexactund ähnlich steht für „Groß- und Kleinschreibung ignorieren“. Es ist nicht für "invers".
Efeu wächst
Es ist erwähnenswert, dass Sie bei Verwendung exclude()mit mehreren Begriffen den Vorschlag möglicherweise mit dem OROperator zusammenstellen möchten , z. B. exclude(Q(field1__queryop1=value1) | Q(field2__queryop2=value2))um die Ergebnisse unter beiden Bedingungen auszuschließen.
Clapas
98

Mit Django 1.7 ist es einfach, eine benutzerdefinierte Suche zu erstellen. Es gibt ein __neSuchbeispiel in der offiziellen Dokumentation von Django .

Sie müssen zuerst die Suche selbst erstellen:

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

Dann müssen Sie es registrieren:

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

Und jetzt können Sie die __neSuche in Ihren Abfragen folgendermaßen verwenden:

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

In Django 1.9 / 1.10 gibt es drei Möglichkeiten.

  1. Kette excludeundfilter

    results = Model.objects.exclude(a=true).filter(x=5)
  2. Verwenden Sie Q()Objekte und den ~Operator

    from django.db.models import Q
    object_list = QuerySet.filter(~Q(a=True), x=5)
  3. Registrieren Sie eine benutzerdefinierte Suchfunktion

    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

    Der register_lookupDekorateur wurde in Django 1.8 hinzugefügt und ermöglicht die benutzerdefinierte Suche wie gewohnt:

    results = Model.objects.exclude(a=True, x__ne=5)
ilse2005
quelle
1
object_list = QuerySet.filter (~ Q (a = True), x = 5): Denken Sie daran, alle anderen Bedingungen, die kein Q enthalten, nach denen beizubehalten, die Q enthalten.
Bhumi Singhal
1
@MichaelHoffmann: A) Sie filtern dann nach dem Ausschluss mit ~ Q nach einem kleineren Datensatz, was effizienter ist. B) wahrscheinlich funktioniert die Sequenzierung umgekehrt nicht .. weiß nicht ... ich erinnere mich!
Bhumi Singhal
41

Während bei den Modellen können Sie mit Filtern =, __gt, __gte, __lt, __lte, können Sie nicht verwenden ne, !=oder <>. Sie können jedoch eine bessere Filterung bei Verwendung des Q-Objekts erzielen.

Sie können eine Verkettung vermeiden QuerySet.filter()und Folgendes QuerySet.exlude()verwenden:

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

Ausstehende Entwurfsentscheidung. In der Zwischenzeit verwendenexclude()

Der Django Issue Tracker hat den bemerkenswerten Eintrag # 5763 mit dem Titel "Queryset hat keinen" ungleichen "Filteroperator" . Es ist bemerkenswert, weil es (Stand April 2016) "vor 9 Jahren eröffnet" (in der Django-Steinzeit), "vor 4 Jahren geschlossen" und "vor 5 Monaten zuletzt geändert" wurde.

Lesen Sie die Diskussion durch, es ist interessant. Grundsätzlich argumentieren manche Leute __nesollten hinzugefügt werden , während andere sagen , exclude()klarer und daher __ne sollte nicht hinzugefügt werden.

(Ich stimme dem ersteren zu, weil das letztere Argument in etwa der Aussage entspricht, dass Python nicht haben sollte, !=weil es hat ==und notbereits ...)

Lutz Prechelt
quelle
21

Verwenden Sie Ausschließen und Filtern

results = Model.objects.filter(x=5).exclude(a=true)
Jincy Mariam
quelle
18

Sie sollten dies verwenden filterund excludemögen

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

Das letzte Codebit schließt alle Objekte aus, bei denen x! = 5 und a True ist. Versuche dies:

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

Denken Sie daran, dass das Zeichen = in der obigen Zeile dem Parameter a False und dem Parameter x die Nummer 5 zuweist. Es geht nicht um Gleichheit. Daher gibt es keine Möglichkeit, das Symbol! = In einem Abfrageaufruf zu verwenden.

Jason Baker
quelle
3
Das ist nicht 100% dasselbe, da es für diese Felder auch Nullwerte geben kann.
MikeN
Dies gibt nur die Elemente zurück, die a = False und x = 5 haben, aber in der Frage würde eine Instanz (a = false, x = 4) enthalten sein.
RemcoGerlich
1
results = Model.objects.filter(a__in=[False,None],x=5)
Jeremy
8

results = Model.objects.filter (a = True) .exclude (x = 5)
Erzeugt diese SQL:
Wählen Sie * aus Tabellex, wobei a! = 0 und x! = 5
Die SQL hängt davon ab, wie Ihr True / False-Feld dargestellt wird, und von der Datenbank-Engine. Der Django-Code ist jedoch alles, was Sie brauchen.

M. Dasn
quelle
8

Django-Modellwerte (Offenlegung: Autor) bieten eine Implementierung der NotEqual- Suche, wie in dieser Antwort . Es bietet auch syntaktische Unterstützung dafür:

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

Was Sie suchen, sind alle Objekte, die entweder a=false oder haben x=5 . |Dient in Django als OROperator zwischen Abfragesätzen:

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

Dies ergibt das gewünschte Ergebnis.

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

für ungleich können Sie ~für eine gleiche Abfrage verwenden. kann natürlich Qverwendet werden, um die gleiche Abfrage zu erreichen.

Milad Khodabandehloo
quelle
Bitte überprüfen Sie die Bearbeitung; Die Verwendung von "und" in Q(a=True) and ~Q(x=5)würde ~Q(x=5)als Argumente für ausgewertet werden .exclude. Bitte lesen Sie: docs.python.org/3/reference/expressions.html#boolean-operations und docs.python.org/3/reference/… .
Zot
2

Achten Sie auf viele falsche Antworten auf diese Frage!

Gerards Logik ist korrekt, gibt jedoch eher eine Liste als einen Abfragesatz zurück (was möglicherweise keine Rolle spielt).

Wenn Sie ein Abfrageset benötigen, verwenden Sie Q:

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