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.

Elisa
sumber

Jawaban:

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

melalui Dokumentasi

Lakshman Prasad
sumber
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 .

hobs
sumber
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


jojo
sumber