Bagaimana melakukan kondisi ATAU di django queryset?


294

Saya ingin menulis permintaan Django yang setara dengan permintaan SQL ini:

SELECT * from user where income >= 5000 or income is NULL.

Bagaimana cara membangun filter queryset Django?

User.objects.filter(income__gte=5000, income=0)

Ini tidak berfungsi, karena itu ANDsaringan. Saya ingin ORfilter untuk mendapatkan gabungan dari masing-masing kueri.


Jawaban:


547
from django.db.models import Q
User.objects.filter(Q(income__gte=5000) | Q(income__isnull=True))

melalui Dokumentasi


Ini akan membantu jika Anda menambahkan print dari object.query sehingga kami dapat menghubungkan output ORM dan Query untuk membiasakannya. BTW contoh yang bagus.
— Eddwin Paz

Apakah lebih baik menggunakan jenis kueri ini atau melakukan dua kueri terpisah?
— MHB

60

Karena QuerySets mengimplementasikan__or__ operator Python ( |), atau gabungan, itu hanya berfungsi. Seperti yang Anda harapkan, para |operator biner mengembalikan QuerySetbegitu order_by(), .distinct()dan filter queryset lainnya dapat tertempel pada akhir.

combined_queryset = User.objects.filter(income__gte=5000) | User.objects.filter(income__isnull=True)
ordered_queryset = combined_queryset.order_by('-income')

Pembaruan 2019-06-20: Ini sekarang sepenuhnya didokumentasikan dalam referensi Django 2.1 QuerySet API . Diskusi yang lebih bersejarah dapat ditemukan di tiket DjangoProject # 21333 .


18
"Tidak berdokumen" dan "warisan" membuatku takut. Saya pikir lebih aman menggunakan objek Q, seperti yang dijelaskan dalam jawaban yang diterima di sini.
— 0atman

2
FYI, order_by () dan berbeda () dapat diterapkan ke queryset pipa setelah mereka digabungkan
— carruthd

@carruthd terima kasih. Saya mengkonfirmasi ini juga. Akan mengedit
— hobs

Bisakah order_by () diterapkan ke setiap queryset individu dan kemudian digabungkan? Sehingga pesanan untuk setiap kondisi tetap dipertahankan? Misalnya, dikombinasikan_queryset = User.objects.filter (income__gte = 5000) .order_by ('income') | User.objects.filter (income__lt = 5000) .order_by ('- income')?
— kebuntuan

2
@Oatman: | Operator didokumentasikan. Lihat docs.djangoproject.com/en/2.0/ref/models/querysets : "Secara umum, objek Q () memungkinkan untuk mendefinisikan dan menggunakan kembali kondisi. Ini memungkinkan pembangunan kueri basis data yang kompleks menggunakan | (OR) dan & ( DAN) operator; khususnya, jika tidak dimungkinkan untuk menggunakan ATAU di QuerySets. " Saya tidak memeriksa dokumentasi untuk versi sebelumnya tetapi operator pipa bekerja dari Django 1.1.4 setidaknya (hanya mencoba).
— pembuatoo

10

Kedua opsi sudah disebutkan dalam jawaban yang ada:

from django.db.models import Q
q1 = User.objects.filter(Q(income__gte=5000) | Q(income__isnull=True))

dan

q2 = User.objects.filter(income__gte=5000) | User.objects.filter(income__isnull=True)

Namun, tampaknya ada beberapa kebingungan mengenai mana yang lebih disukai.

Intinya adalah bahwa mereka identik pada tingkat SQL , jadi jangan ragu untuk memilih mana yang Anda suka!

The Django ORM Cookbook berbicara secara rinci tentang ini, ini adalah bagian yang relevan:


queryset = User.objects.filter(
        first_name__startswith='R'
    ) | User.objects.filter(
    last_name__startswith='D'
)

mengarah ke

In [5]: str(queryset.query)
Out[5]: 'SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login",
"auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name",
"auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff",
"auth_user"."is_active", "auth_user"."date_joined" FROM "auth_user"
WHERE ("auth_user"."first_name"::text LIKE R% OR "auth_user"."last_name"::text LIKE D%)'

dan

qs = User.objects.filter(Q(first_name__startswith='R') | Q(last_name__startswith='D'))

mengarah ke

In [9]: str(qs.query)
Out[9]: 'SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login",
 "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name",
  "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff",
  "auth_user"."is_active", "auth_user"."date_joined" FROM "auth_user"
  WHERE ("auth_user"."first_name"::text LIKE R% OR "auth_user"."last_name"::text LIKE D%)'

sumber: django-orm-cookbook


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.