Django masuk / tidak dalam kueri

100

Saya mencoba mencari cara untuk menulis kueri gaya 'tidak dalam' di django. Misalnya, struktur kueri yang saya pikirkan akan terlihat seperti ini.

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

Seperti apa sintaks django yang mengasumsikan model disebut table1 dan table2?

Turbo
sumber

Jawaban:

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

Fungsi pengecualian berfungsi seperti Notoperator yang Anda minta. Atribut flat = Truememberitahu table2query untuk mengembalikan value_listsebagai daftar satu tingkat. Jadi ... pada akhirnya Anda mendapatkan daftar IDsdari table2, yang akan Anda gunakan untuk menentukan kondisi di dalam table1, yang akan ditolak oleh fungsi pengecualian.

Harph
sumber
3
Saya juga mengalami masalah dengan konstruktor daftar [tabel2 ...] -> daftar (tabel2 ...) bekerja untuk saya.
RickyA
3
koreksi: table1.objects.exclude (id__in = table2.objects.filter (your_condition) .values_list ('id', flat = True))
Richard
1
Mencoba menggunakan solusi ini dan mengalami masalah, jadi jika terjadi pada orang lain ... Objs=Tbl1.objects.filter(...); IDs=Objs.values_list('id', flat=True); Objs.delete(); Tbl2.objects.filter(id__in=IDs')Ini tidak berhasil karena ID sebenarnya adalah objek QuerySet. Ketika saya menghapus baris asalnya, itu tidak lagi berfungsi dengan kueri lain. Solusinya adalah Tbl2.objects.filter(id__in=list(IDs))- mengubahnya menjadi daftar
Dakusan
1
Bergantung pada konteksnya, jika filter seperti "memiliki hitungan (xx) == yy", ini lebih dari 100x lebih cepat untuk digunakan annotate()(waktu itu memberi saya 1.0497902309998608 vs 0.00514069400014705)
Olivier Pons
10

dengan model ini:

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

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

Anda harus mendapatkan apa yang ingin Anda gunakan:

table1.objects.exclude(table2=some_param)
Sergio Morstabilini
sumber
1
Ini masih membuat Anda berpotensi menarik BANYAK record dari db jika tidak perlu.
Jay Taylor
5
table1.objects.extra(where=["table1.id NOT IN (SELECT table2.key_to_table1 FROM table2 WHERE table2.id = some_parm)"])
ibz
sumber
1

Anda dapat menulis pencarian kustom untuk query Django:

Dari dokumentasi : "Mari kita mulai dengan kebiasaan lookup sederhana Kami akan menulis kustom lookup ne yang bekerja berlawanan dengan membalas . Author.objects.filter (name__ne = 'Jack') akan menerjemahkan ke 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
sumber
-16
[o1 for o1 in table1.objects.all() if o1.id not in [o2.id for o2 in table2.objects.filter(id=some_parm)]]

Atau lebih baik

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]
Paprika Biru
sumber
12
Mengulangi setiap baris dalam tabel. gg
Rebs