Bagaimana saya melakukan yang tidak sama dalam penyaringan queryset Django?

666

Dalam model Django QuerySets, saya melihat bahwa ada __gtdan __ltuntuk nilai komparatif, tetapi apakah ada __ne/ !=/ <>( tidak sama dengan ?)

Saya ingin menyaring menggunakan yang tidak sama:

Contoh:

Model:
    bool a;
    int x;

saya ingin

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

The !=tidak sintaks yang benar. Aku mencoba __ne, <>.

Saya akhirnya menggunakan:

results = Model.objects.exclude(a=true, x__lt=5).exclude(a=true, x__gt=5)
MikeN
sumber
75
Apakah hasil = Model.objects.exclude (a = true) .filter (x = 5) telah berfungsi?
hughdbrown
3
@ hughdbrown. Tidak. Permintaan Anda mengecualikan semua a=trueterlebih dahulu dan kemudian menerapkan x=5filter pada sisanya. Permintaan yang dimaksud hanya membutuhkan yang dengan a=truedan x!=5. Perbedaannya adalah bahwa semua orang dengan a=truedan x=5juga disaring.
Mitchell van Zuylen

Jawaban:

690

Mungkin objek Q bisa membantu untuk masalah ini. Saya tidak pernah menggunakan mereka tetapi tampaknya mereka dapat dinegasikan dan dikombinasikan seperti ekspresi python normal.

Pembaruan: Saya Baru mencobanya, sepertinya berfungsi cukup baik:

>>> 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
sumber
16
@ JCLeitão: lihat juga jawaban @ d4nt di bawah ini untuk sintaksis yang lebih intuitif.
Paul D. Waite
611

Kueri Anda tampaknya memiliki negatif ganda, Anda ingin mengecualikan semua baris di mana x tidak 5, jadi dengan kata lain Anda ingin memasukkan semua baris di mana x IS 5. Saya percaya ini akan melakukan trik.

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

Untuk menjawab pertanyaan spesifik Anda, tidak ada "tidak sama dengan" tetapi itu mungkin karena Django memiliki kedua metode "filter" dan "kecualikan" yang tersedia sehingga Anda selalu dapat dengan mudah mengubah putaran logika untuk mendapatkan hasil yang diinginkan.

d4nt
sumber
2
@ d4nt: Saya mungkin salah, tapi saya pikir pertanyaannya seharusnyaresults = Model.objects.filter(a=true).exclude(x=5)
Taranjeet
1
@Taranjeet: Saya pikir Anda salah membaca kueri asli. Versi d4nt benar, karena OP ingin mengecualikan (a = Benar) dan meniadakan pengecualian x = 5 (yaitu termasuk itu).
Chuck
3
Saya pikir ini salah karena sebuah instance (x = 4, a = false) akan dikecualikan secara salah.
RemcoGerlich
4
@danigosa Sepertinya itu tidak benar. Saya hanya mencoba ini sendiri, dan urutan excludedan filterpanggilan tidak membuat perbedaan yang berarti. Urutan kondisi dalam WHEREklausa berubah, tetapi bagaimana masalahnya?
coredumperror
4
@danigosa urutan pengecualian dan filter tidak masalah.
EralpB
132

yang field=valuesintaks dalam permintaan adalah singkatan untuk field__exact=value. Artinya, Django menempatkan operator kueri di bidang kueri di pengidentifikasi . Django mendukung operator berikut:

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

Saya yakin dengan menggabungkan ini dengan objek Q seperti yang disarankan Dave Vogt dan menggunakan filter()atau exclude()seperti yang disarankan Jason Baker, Anda akan mendapatkan apa yang Anda butuhkan untuk pertanyaan apa pun yang mungkin.

SingleNegationElimination
sumber
terima kasih ini luar biasa. Saya menggunakan beberapa hal seperti ini tg=Tag.objects.filter(user=request.user).exclude(name__regex=r'^(public|url)$')dan itu berfungsi.
suhailvs
@suhail, harap diingat bahwa tidak semua database mendukung sintaks regex :)
Anoyz
2
i in icontains, iexactdan singkatan yang sama untuk "abaikan sensitivitas huruf". Ini bukan untuk "terbalik".
Ivy Growing
Perlu dicatat bahwa ketika Anda menggunakan exclude()dengan beberapa istilah, Anda mungkin ingin membuat proposisi dengan ORoperator, misalnya exclude(Q(field1__queryop1=value1) | Q(field2__queryop2=value2))untuk mengecualikan hasil dalam kedua kondisi.
clapas
98

Sangat mudah untuk membuat pencarian kustom dengan Django 1.7. Ada __necontoh pencarian dalam dokumentasi resmi Django .

Anda harus membuat pencarian itu sendiri terlebih dahulu:

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

Maka Anda perlu mendaftarkannya:

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

Dan sekarang Anda dapat menggunakan __nepencarian di pertanyaan Anda seperti ini:

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

Di Django 1.9 / 1.10 ada tiga opsi.

  1. Rantai excludedanfilter

    results = Model.objects.exclude(a=true).filter(x=5)
  2. Gunakan Q()benda dan ~operator

    from django.db.models import Q
    object_list = QuerySet.filter(~Q(a=True), x=5)
    
  3. Daftarkan fungsi pencarian kustom

    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
    

    The register_lookupdekorator ditambahkan pada Django 1,8 dan memungkinkan kustom lookup seperti biasa:

    results = Model.objects.exclude(a=True, x__ne=5)
ilse2005
sumber
1
object_list = QuerySet.filter (~ Q (a = True), x = 5): Ingatlah untuk menjaga semua kondisi lainnya tidak mengandung Q setelah yang mengandung Q.
Bhumi Singhal
1
@MichaelHoffmann: A) Anda kemudian akan memfilter pada set data yang lebih kecil setelah pengecualian menggunakan ~ Q jadi lebih efisien. B) mungkin urutan sebaliknya tidak berfungsi .. tidak tahu .. tidak ingat!
Bhumi Singhal
41

Sementara dengan Model, Anda dapat menyaring dengan =, __gt, __gte, __lt, __lte, Anda tidak dapat menggunakan ne, !=atau <>. Namun, Anda dapat mencapai pemfilteran yang lebih baik dalam menggunakan objek Q.

Anda dapat menghindari rantai QuerySet.filter()dan QuerySet.exlude(), dan gunakan ini:

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

Keputusan desain yang tertunda. Sementara itu, gunakanexclude()

Pelacak masalah Django memiliki entri luar biasa # 5763 , berjudul "Queryset tidak memiliki operator filter" tidak sama "" . Ini luar biasa karena (per April 2016) "dibuka 9 tahun yang lalu" (di zaman batu Django), "ditutup 4 tahun lalu", dan "terakhir diubah 5 bulan yang lalu".

Bacalah diskusi, itu menarik. Pada dasarnya, beberapa orang berdebat __neharus ditambahkan sementara yang lain mengatakan exclude()lebih jelas dan karenanya __ne harus tidak ditambahkan.

(Saya setuju dengan yang pertama, karena argumen terakhir kira-kira sama dengan mengatakan Python seharusnya tidak ada !=karena sudah ==dan notsudah ...)

Lutz Prechelt
sumber
22

Menggunakan kecualikan dan filter

results = Model.objects.filter(x=5).exclude(a=true)
jincy mariam
sumber
18

Anda harus menggunakan filterdan excludemenyukai ini

results = Model.objects.exclude(a=true).filter(x=5)
Kehabisan waktu
sumber
8

Bit kode terakhir akan mengecualikan semua objek di mana x! = 5 dan a adalah Benar. Coba ini:

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

Ingat, tanda = pada baris di atas memberikan False ke parameter a dan angka 5 ke parameter x. Itu tidak memeriksa kesetaraan. Jadi, sebenarnya tidak ada cara untuk menggunakan simbol! = Dalam panggilan kueri.

Jason Baker
sumber
3
Itu bukan 100% hal yang sama karena mungkin juga ada nilai Null untuk bidang tersebut.
MikeN
Ini mengembalikan hanya item-item yang memiliki = Salah dan x = 5, tetapi dalam pertanyaan contoh (a = salah, x = 4) akan dimasukkan.
RemcoGerlich
1
results = Model.objects.filter(a__in=[False,None],x=5)
Jeremy
8

hasil = Model.objects.filter (a = Benar) .exclude (x = 5)
Hasilkan sql ini:
pilih * dari tablex di mana a = = dan x! = 5
Sql tergantung pada bagaimana bidang Benar / Salah Anda diwakili, dan mesin basis data. Kode Django adalah semua yang Anda butuhkan.

M. Dasn
sumber
8

Django-model-values (pengungkapan: penulis) menyediakan implementasi pencarian NotEqual , seperti dalam jawaban ini . Ini juga menyediakan dukungan sintaksis untuk itu:

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

Apa yang Anda cari adalah semua objek yang memiliki a=false atau x=5 . Di Django, |berfungsi sebagai ORoperator antara kueri:

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

Ini akan memberikan hasil yang Anda inginkan.

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

karena tidak sama Anda dapat menggunakan ~kueri yang sama. jelas, Qdapat digunakan untuk mencapai kueri yang sama.

Milad Khodabandehloo
sumber
Silakan periksa hasil edit; menggunakan "dan" dalam Q(a=True) and ~Q(x=5)akan mengevaluasi ~Q(x=5)sebagai argumen untuk .exclude. Silakan baca: docs.python.org/3/reference/expressions.html#boolean-operations dan docs.python.org/3/reference/… .
tzot
2

Waspadai banyak jawaban yang salah untuk pertanyaan ini!

Logika Gerard benar, meskipun itu akan mengembalikan daftar daripada queryset (yang mungkin tidak masalah).

Jika Anda membutuhkan queryset, gunakan Q:

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