Bagaimana saya melakukan filter ATAU dalam permintaan Django?


303

Saya ingin dapat membuat daftar item yang ditambahkan oleh pengguna (terdaftar sebagai pencipta) atau item telah disetujui.

Jadi pada dasarnya saya perlu memilih:

item.creator = owner or item.moderated = False

Bagaimana saya melakukan ini di Django? (lebih disukai dengan filter atau queryset).

Jawaban:


545

Ada Qobjek yang memungkinkan pencarian yang rumit. Contoh:

from django.db.models import Q

Item.objects.filter(Q(creator=owner) | Q(moderated=False))

6
bagaimana ini bisa dilakukan secara terprogram? Jadi, misalnya dapat memilikifor f in filters: Item.objects.filter(Q(creator=f1) | Q(creator=f2) | ...)
Alexis

14
@AlexisK Gunakan sesuatu seperti reduce(lambda q, f: q | Q(creator=f), filters, Q())untuk membuat objek Q besar.
Phob

24
@alexis: Anda juga bisa melakukannya Item.objects.filter(creator__in=creators), misalnya.
Kevin London

4
Jika Anda bertanya-tanya (seperti saya) di mana |digunakan sebagai operator OR berasal, itu sebenarnya adalah operator serikat pekerja. Ini juga digunakan (bukan di sini) sebagai bitwise ATAU: stackoverflow.com/questions/5988665/pipe-character-in-python
e100

124

Anda dapat menggunakan | operator untuk menggabungkan queryset secara langsung tanpa memerlukan objek Q:

result = Item.objects.filter(item.creator = owner) | Item.objects.filter(item.moderated = False)

(sunting - Saya awalnya tidak yakin apakah ini menyebabkan permintaan tambahan tetapi @spookylukey menunjukkan bahwa evaluasi queryset yang malas menangani hal itu)


4
Untuk mengetahui kueri mana yang dieksekusi berdasarkan permintaan yang diberikan, Anda dapat menggunakan aplikasi Django debug-toolbar. Itu dibuat luar biasa dan menang.
Deniz Dogan

25
lakukan 'dari koneksi impor django.db' dan gunakan 'connection.queries'. Ini membutuhkan DEBUG = Benar. BTW, Anda harus tahu bahwa QuerySets malas dan ini hanya mengenai DB.
spookylukey

1
Bisakah pengecualian digunakan dengan perbandingan yang dinegasikan?
Neob91

2
dapatkah ini menghasilkan duplikat dalam kueryset hasil?
Charles Haro

1
Lebih khusus kumpulan permintaan cenderung untuk memukul DB hanya ketika Anda mencoba untuk mengindeks ke dalamnya, jika tidak, Anda hanya membangun permintaan.
awiebe

41

Perlu dicatat bahwa mungkin untuk menambahkan ekspresi Q.

Sebagai contoh:

from django.db.models import Q

query = Q(first_name='mark')
query.add(Q(email='mark@test.com'), Q.OR)
query.add(Q(last_name='doe'), Q.AND)

queryset = User.objects.filter(query)

Ini berakhir dengan permintaan seperti:

(first_name = 'mark' or email = 'mark@test.com') and last_name = 'doe'

Dengan cara ini tidak perlu berurusan dengan atau operator, mengurangi dll.


2
Tetapi lebih mudah menulis query |= Q(email='mark@test.com')?
Alex78191

26

Anda ingin membuat filter dinamis maka Anda harus menggunakan seperti Lambda

from django.db.models import Q

brands = ['ABC','DEF' , 'GHI']

queryset = Product.objects.filter(reduce(lambda x, y: x | y, [Q(brand=item) for item in brands]))

reduce(lambda x, y: x | y, [Q(brand=item) for item in brands]) setara dengan

Q(brand=brands[0]) | Q(brand=brands[1]) | Q(brand=brands[2]) | .....

6
Jawaban sempurna untuk saya! Untuk python3, lakukan from functools import reducesebelumnya.
Dharmit

1
Mengapa tidak menggunakan operator.or_bukan lambda x, y: x | y?
Alex78191

20

Mirip dengan answerera yang lebih tua, tetapi sedikit lebih sederhana, tanpa lambda:

filter_kwargs = {
    'field_a': 123,
    'field_b__in': (3, 4, 5, ),
}

Untuk memfilter kedua kondisi ini menggunakan OR:

Item.objects.filter(Q(field_a=123) | Q(field_b__in=(3, 4, 5, ))

Untuk mendapatkan hasil yang sama secara terprogram:

list_of_Q = [Q(**{key: val}) for key, val in filter_kwargs.items()]
Item.objects.filter(reduce(operator.or_, list_of_Q))

(Dipecah dalam dua baris di sini, untuk kejelasan)

operatorada di perpustakaan standar: import operator
Dari docstring:

or_ (a, b) - Sama dengan | b.

Untuk Python3, reducebukan builtin lagi tetapi masih di perpustakaan standar:from functools import reduce


PS

Jangan lupa pastikan list_of_Qtidak kosong - reduce()akan tersedak daftar kosong, perlu setidaknya satu elemen.


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.