🐣

【Django】2つ以上のクエリセットを結合する方法

2022/11/03に公開約2,200字

この記事では、Djangoが備えている「2つ以上のクエリセットを結合する方法」について解説します。

公式ドキュメントを覗く 🫣

公式ドキュメントにはこのように書かれています。

union(*other_qs, all=False)

SQLのUNION演算子を使用して、2つ以上のQuerySetの結果を結合します。
重複する値を許可する場合は、引数allに対してTrueをセットします。

qs1.union(qs_2, qs_3)

union()は、引数が他のモデルのクエリセットであっても、最初のクエリセットの型のモデルインスタンスを返します。異なるモデルを渡しても、同じフィールドとデータ型を持つもの同士であれば結合することができます。(*)
(*: 型の並びが同じであればフィールド名は重要ではない)

qs1 = Author.objects.values_list('name')
qs2 = Entry.objects.values_list('headline')
qs1.union(qs2).order_by('name')

また、結果として得られるクエリセットでは、LIMIT, OFFSET, COUNT, ORDER BY および列の指定(スライス, count(), exists(), order_by(), values()/values_list())のみが許可されている。

結論 ⚖️

union()を使うことで、2つ以上のクエリセットをマージ(結合)することができます。

使い方 💪

もっとも一般的な使い方で、次のような3つのクエリセットを生成して結合を行います。

class Employee(models.Model):
    name = models.CharField(max_length=255)
    employee_id = models.CharField(max_length=255)
    birthday = models.Date()
    hire_date = models.Date()
    birth_place = models.CharField(max_length=50)

class Client(models.Model):
    name = models.CharField(max_length=255)
    email = models.EmailField(max_length=255)
    phone_number = models.CharField(max_length=11)
    birth_place = models.CharField(max_length=50)
# 出身地が東京都の社員
qs_1 = Employee.objects.filter(birth_place='東京都')
# 出身地が神奈川県の社員
qs_2 = Employee.objects.filter(birth_place='神奈川県')
# 出身地が埼玉県の社員
qs_3 = Employee.objects.filter(birth_place='埼玉県')

# 結合(出身地が東京、神奈川、埼玉の社員)
employee_kanto_qs = qs_1.union(q2, q3)

これはエラーになります。

# 出身地が東京都の顧客
qs_4 = Client.objects.filter(birth_place='東京都')
# 結合(出身地が東京都の社員と顧客)
tokyo_qs = qs_1.union(qs_4)
# エラー
>> django.db.utils.OperationalError: SELECTs to the left and right of UNION do not have the same number of result columns

和集合union()の操作では、同じデータ型の並び順をもつクエリセットに対してのみ行うことができるため、上記のようにエラーが発生しました。

次のようにすることで異なるモデルから取得したクエリセットも結合することができます。

qs_1 = Employee.objects.filter(birth_place='東京都').values_list(
                                                    'name', 'birth_place'
						    )
qs_4 = Client.objects.filter(birth_place='東京都').values_list(
                                                    'name', 'birth_place'
						    )
# 結合
tokyo_qs = qs_1.union(qs_4)

Discussion

ログインするとコメントできます