2つ以上のクエリセットを連結する方法の一つに、for文を使って一つづつ結果格納用のリストに追加する方法がありますが、処理時間がかかります。
今回は2つ以上のクエリセットをfor文を使わないで連結する方法を解説します。
同じモデルを連結したい場合
重複なし
同じモデルを重複なしで連結したい場合、OR ( | ) 演算子を使う方法と、union()を使う方法があります。
※qs1, qs2, qs3変数には同じモデルのクエリセットが代入されているとします。
# | を使う方法
result = qs1 | qs2 | qs3
# union()を使う方法
result = qs1.union(qs2, qs3)
” | を使う方法”の方がコードがスッキリしていますが並び替えなどもしたい場合は”union()を使う方法”がおすすめです。
以下の様にunion()の結果に対してorder_by()が使えます。他にもexists(), count(), values_list(), values(), スライスなどが使えます。
result = qs1.union(qs2, qs3).order_by('name')
重複あり
重複を許可したい場合は、union()のキーワード引数にall=Trueを指定します。
result = qs1.union(qs2, qs3, all=True)
異なるモデルを連結したい場合
用途は少ないですが、異なるモデルのクエリセットを連結する方法もいくつか紹介します。
重複なし
その1 特定のカラムを結合したい場合
union()は異なるモデルセットでも、以下の様にvalues_listを使って要素数を同じにすれば連結が可能です。
以下の場合は、Userモデルの’username’カラムとGroupモデルの’name’カラムを結合しています。
仮に、usernameとnameが同じ場合は重複扱いされます。
users = User.objects.filter(id__lt=3).values_list('username')
groups = Group.objects.filter(id__lt=3).values_list('name')
result_list = users.union(groups)
その2 クエリセットの要素を維持したい場合
itertoolsモジュールのchainメソッドを使って連結する事ができます。
同じモデルのクエリセットでunion()を使って重複を除いたクエリセットを作成後、chainメソッドを使って連結できます。
from itertools import chain
# result1とresult2は異なるモデルのクエリセット
result1 = qs1_1.union(qs1_2) # qs1_1, qs1_2は同じモデルのクエリセット
result2 = qs2_1.union(qs2_2) # qs2_1, qs2_2は同じモデルのクエリセット
result_list = list(chain(result1, result2))
重複あり
その1特定のカラムを結合したい場合
基本的には重複なしと同じ方法ですが、重複を許可したい場合は、union()のキーワード引数にall=Trueを指定します。
users = User.objects.filter(id__lt=3).values_list('username')
groups = Group.objects.filter(id__lt=3).values_list('name')
result_list = users.union(groups, all=True)
その2 クエリセットの要素を維持したい場合
itertoolsモジュールのchainメソッドを使って連結する事ができます。
sortedメソッドを使うと1ライナーで並び替えもできますが、異なるモデルのため正しい結果になっているか注意が必要です。
※qs1, qs2, qs3変数には異なるモデルのクエリセットが代入されているとします。
from itertools import chain
result_list = list(chain(qs1, qs2, qs3))
# sortedを使って並び替え
result_list = sorted(chain(qs1, qs2, qs3), key=lambda obj: obj.date_created)
まとめ
同じモデルのクエリセットを連結したいときは、union()やOR( | )演算子を使って簡単に連結できます。
異なるモデルのクエリセットは使用用途が少なくコードが複雑になりやすいので、なるべくシンプルなコーディングを心がけたいです。
コメントを残す