Django w / nie w zapytaniu

100

Próbuję dowiedzieć się, jak napisać zapytanie w stylu „nie w” w django. Na przykład struktura zapytania, o której myślę, wyglądałaby tak.

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

Jak wyglądałaby składnia django przy założeniu modeli o nazwach tabela1 i tabela2?

Turbo
źródło

Odpowiedzi:

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

Funkcja wykluczania działa jak Notoperator, o który prosisz. Atrybut flat = Truenakazuje table2zapytaniu zwrócić listę value_listjako jednopoziomową. Więc ... na koniec otrzymujesz listę IDsz tabeli2, w której zamierzasz zdefiniować warunek table1, który zostanie odrzucony przez funkcję wykluczania.

Harph
źródło
3
Miałem też kłopoty z konstruktorem list [tablica2 ...] -> lista (tablica2 ...) działała u mnie.
RickyA
3
korekta: table1.objects.exclude (id__in = table2.objects.filter (your_condition) .values_list ('id', flat = True))
Richard
1
Próbowałem użyć tego rozwiązania i napotkałem problem, więc jeśli zdarzy się to komuś innemu ... Objs=Tbl1.objects.filter(...); IDs=Objs.values_list('id', flat=True); Objs.delete(); Tbl2.objects.filter(id__in=IDs')To nie zadziałało, ponieważ IDs to w rzeczywistości obiekt QuerySet. Kiedy usunąłem wiersze, z których pochodzi, nie działało już z innymi zapytaniami. Rozwiązaniem jest Tbl2.objects.filter(id__in=list(IDs))-
zamień
1
W zależności od kontekstu, jeśli filtr ma annotate()postać „mając count (xx) == yy”, jego użycie jest ponad 100 razy szybsze (czas dał mi 1,0497902309998608 w porównaniu z 0,00514069400014705)
Olivier Pons
10

z tymi modelami:

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

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

powinieneś dostać to, czego chcesz, używając:

table1.objects.exclude(table2=some_param)
Sergio Morstabilini
źródło
1
To nadal powoduje, że możesz niepotrzebnie pobierać DUŻO rekordów z bazy danych.
Jay Taylor
5
table1.objects.extra(where=["table1.id NOT IN (SELECT table2.key_to_table1 FROM table2 WHERE table2.id = some_parm)"])
ibz
źródło
1

Możesz napisać własne wyszukiwanie zapytań Django:

Z dokumentacji : „Zacznijmy od prostego niestandardowego wyszukiwania . Napiszemy niestandardowe wyszukiwanie ne, które działa odwrotnie do dokładnego . Author.objects.filter (name__ne = 'Jack') przetłumaczy na SQL: "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
źródło
-16
[o1 for o1 in table1.objects.all() if o1.id not in [o2.id for o2 in table2.objects.filter(id=some_parm)]]

Albo lepiej

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]
Niebieskie Papryki
źródło
12
Iterowanie po każdym wierszu w tabeli. gg
Rebs