Qオブジェクトってなに?
最近、業務中にこんな感じのコードに出会いました。
Contract.objects.filter(Q(price__gt=100)|Q(date__year = 2022))
なんとなくor
を使って絞り込みたいの時に使われることは知っていたのですが、もう少し深ぼって調べてみました。
1.ドキュメントによると
Q object (django.db.models.Q) is an object used to encapsulate a collection of keyword arguments. These keyword arguments are specified as in "Field lookups" above.
訳すると、
Qオブジェクトはキーワード引数の集まりをカプセル化するときに使われるオブジェクト。これらのキーワード引数は、上の Field lookups
のように指定される。
ということらしいです。よくわからないですね。^^
基本的にfilter(条件,条件)の場合はANDで絞り込まれるますが、ANDではなくORを使って絞り込みたい時にQオブジェクトが役に立つという認識で大丈夫です。
Field lookups
というのは、
example1 = Model.objects.get(question__startswith='Who')
example2 = Model.objects.filter(num__gt = 20)
上の__アンダースコア✖️2の後に続く部分のことです。ここでいうとstartswith,gtのことですね。
要は、Field lookupを使うときにQオブジェクトを組み合わせることで、より柔軟に絞り込みができるということです。Field lookupによる絞り込みとAND(,
による区切り),ORがあればある程度のことはできますね。
しかしAND,OR
だけだと、ただの和積なのでそんなに柔軟にはなりません。そこでもう一つ、NOT(否定)
もちゃんと使えるようになっています。
2.NOT のやり方
NOTを使いたい時は、~
を使います。
次のように、Qの前に~
をつければ出来上がりです。
#Whoから始まる文字列、または、2005年でないという条件にしたいときに
Q(question__startswith='Who') | ~Q(pub_date__year=2005)
3.注意点
Lookup functions can mix the use of Q objects and keyword arguments. All arguments provided to a lookup function (be they keyword arguments or Q objects) are "AND"ed together. However, if a Q object is provided, it must precede the definition of any keyword arguments
lookup関数を使うときは、Qオブジェクトを先に書かないと望んだクエリと違うクエリが出されちゃうよということです。もし、Qオブジェクトを後に使った場合は、OR
ではなくAND
で組み合わさることになるので気をつけてください。
#Qが先に来ているので、OK
Poll.objects.get(
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
question__startswith='Who',
)
#Qが後に来ているので、NG
Poll.objects.get(
question__startswith='Who',
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)
どんなSQLが出されているか
エンジニアとしては、どんなSQLが出されているか知っておくに越したことはないので一応いくつか例を出しておきますね。
Django>
Q(question__startswith='Who') | Q(question__startswith='What')
SQL>
WHERE question LIKE 'Who%' OR question LIKE 'What%'
Django>
Poll.objects.get(
Q(question__startswith='Who'),
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)
SQL>
SELECT * from polls WHERE question LIKE 'Who%'
AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
参考記事