Django in / nicht in Abfrage

100

Ich versuche herauszufinden, wie man eine Abfrage im "Nicht in" -Stil in Django schreibt. Zum Beispiel würde die Abfragestruktur, an die ich denke, so aussehen.

select table1.* 
from table1
where table1.id not in 
(
  select table2.key_to_table1
  from table2 
  where table2.id = some_parm 
)

Wie würde die Django-Syntax unter der Annahme von Modellen mit den Namen table1 und table2 aussehen?

Turbo
quelle

Antworten:

162
table1.objects.exclude(id__in=
    table2.objects.filter(your_condition).values_list('id', flat=True))

Die Ausschlussfunktion funktioniert wie der NotOperator, nach dem Sie gefragt haben. Das Attribut flat = Trueweist an die table2Abfrage an, die value_listListe als einstufig zurückzugeben. Also ... am Ende erhalten Sie eine Liste von IDsaus Tabelle2, in der Sie die Bedingung definieren, in der der Benutzer table1die Ausschlussfunktion ablehnt .

Harph
quelle
3
Ich hatte auch Probleme mit dem Listenkonstruktor [Tabelle2 ...] -> Liste (Tabelle2 ...), der für mich funktioniert hat.
RickyA
3
Korrektur: table1.objects.exclude (id__in = table2.objects.filter (your_condition) .values_list ('id', flat = True))
Richard
1
Ich habe versucht, diese Lösung zu verwenden, und bin auf ein Problem Objs=Tbl1.objects.filter(...); IDs=Objs.values_list('id', flat=True); Objs.delete(); Tbl2.objects.filter(id__in=IDs')gestoßen. Wenn es also jemand anderem passiert ... Dies hat nicht funktioniert, da IDs tatsächlich ein QuerySet-Objekt sind. Als ich die Zeilen löschte, aus denen es stammte, funktionierte es nicht mehr mit anderen Abfragen. Die Lösung ist Tbl2.objects.filter(id__in=list(IDs))- verwandeln Sie es in eine Liste
Dakusan
1
Je nach Kontext ist der Filter mehr als annotate()100-mal schneller zu verwenden (timeit gab mir 1.0497902309998608 vs 0.00514069400014705)
Olivier Pons
10

mit diesen Modellen:

class table1(models.Model):
    field1 = models.CharField(max_length=10)      # a dummy field

class table2(models.Model):
    key_to_table1 = models.ForeignKey(table1)

Sie sollten bekommen, was Sie wollen mit:

table1.objects.exclude(table2=some_param)
Sergio Morstabilini
quelle
1
Dadurch können Sie möglicherweise immer noch unnötig viele Datensätze aus der Datenbank abrufen.
Jay Taylor
5
table1.objects.extra(where=["table1.id NOT IN (SELECT table2.key_to_table1 FROM table2 WHERE table2.id = some_parm)"])
ibz
quelle
1

Sie können eine benutzerdefinierte Suche für Django-Abfragen schreiben:

Aus der Dokumentation : „Beginnen wir mit einer einfachen benutzerdefinierten Lookup Wir werden eine benutzerdefinierte Nachschlag schreiben ne , die gegenüber arbeitet , um exakt . Author.objects.filter (name__ne =‚Jack‘) wird dem SQL übersetzen: "author"."name" <> 'Jack'

from django.db.models import 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
Blairg23
quelle
-16
[o1 for o1 in table1.objects.all() if o1.id not in [o2.id for o2 in table2.objects.filter(id=some_parm)]]

Oder besser

not_in_ids = [obj.id for obj in table2.objects.filter(id=some_parm)]
selected_objects = [obj for obj in table1.objects.iterator() if obj.id not in not_in_ids]
Blue Peppers
quelle
12
Durch jede Zeile in einer Tabelle iterieren. gg
Rebs