概要
Djangoにおけるクエリセットの和集合「|
」を使ってみました。
サンプルコードで解説します。
前提
以下のように、国に関するDBテーブルを作成。
サンプルコード
以下のようにして使います。
ここでは、アジアとヨーロッパの国を取得するクエリセットを作成し、その後に和集合を利用して結合しています。
# from django.db import models
from response.models import Country
asia_countries = Country.objects.filter(continent='Asia')
europe_countries = Country.objects.filter(continent='Europe')
countries = asia_countries | europe_countries
print(countries)
## 出力結果 ##
## <QuerySet [<Country: Country object (1)>, <Country: Country object (3)>, <Country: Country object (4)>, <Country: Country object (5)>, <Country: Country object (6)>]>
for country in countries:
print(country.name)
## 出力結果 ##
# Japan
# France
# United Kingdom
# Germany
# China
上記の通り、クエリセットの出力だけでは、国名などのデータは表せないので、country.name
のようにしてあげれば出力できます。
和集合とは?
和集合とは 2 つの集合の少なくともどちらか 1 つに含まれる要素の集合のこと。
Djangoのクエリセットに対する和集合(ユニオン)操作は、2つ以上のクエリセットから要素を取得する必要がある場合に便利です。上記のように「アジアとヨーロッパ」の両方の国を一度に取得したい場合など。
ちなみに、
countries = asia_countries.union(europe_countries)
という書き方でも可能です。
同様に、以下の集合演算では下記のように使用します。
積集合:&
またはintersection()
差集合:-
またはdifference()
対称差集合:^
またはsymmetric_difference
クエリセットを利用する事例
クエリセットのままの方が使いやすい、という簡単な事例として、Country
モデルと関連づけられたモデルのデータを取得したいケースがあります。
data = countries.select_related('capital', 'language').values('capital__area', 'language__language_abbreviation_name')
select_related
は、データベースのJOIN操作を行い、関連するオブジェクトを一度のクエリで取得するメソッド。私は、外部キーを通じて関連付けられたモデルに対して使用するのが多いです。DBへのクエリ数も減少しますので。
values
メソッドは、指定したフィールドの値を取得できます。指定したフィールドは、Country
モデルまたはCapital
、Language
モデルのフィールドである必要があります。返すのは、モデルのインスタンスではなく、辞書のリストなので、取ってきたものを何か処理したい場合に使えます。
これにより、Country
モデルと関連するCapital
とLanguage
モデルから、それぞれ指定したフィールドの値を取得できるようになります。
select_related
(とvalues
メソッド)を紹介した記事は以前書いたのでご参考までに。
あと、values
やvalues_list
、first
やdistinct
がそれぞれ何を返すんだ?みたいになることがあったので、以下記事でもそれらをまとめました。
(補足)和集合を活用できるケース
上記サンプルコードでは、和集合に限らずとも実現できると思います。
しかし、とある条件のfor
文でぐるぐるクエリセットを回しながら、さらに指定した条件に該当したもののみを、空のクエリセットに入れてあげる、という処理の時には役に立つと思いました。
append()
や+=
のイメージです。
これらはクエリセットに対しては使えないので、和集合が活用できます。
asia_and_europe_countries = Country.objects.filter(continent__in=['Asia', 'Europe'])
query_set = Country.objects.none()
for country in asia_and_europe_countries.values('language', 'currency').distinct():
selected_countries = asia_and_europe_countries.filter(
language=country['language'],
currency=country['currency'],
)
ng_countries = selected_countries.filter(xx_flag='NG')
ok_countries = selected_countries.filter(xx_flag='OK').exists()
if ng_countries.exists() and not ok_countries:
query_set = query_set | ng_countries
else :
# 略....
上記の場合、
「アジアおよびヨーロッパにある国々」の中で、「同じ言語と通貨を使った国々」のうち、「xx_flag
がNGの国」しか存在しない場合、その国(国々)をquery_set
に追加していっています。つまり、とあるグルーピングの中で条件指定を行い、さらにそのグループの中のレコードのフラグが全て立っていた場合のみ、それ(それら)を抽出しています。
例えば、「アジアおよびヨーロッパ」にある「hoge語」と「foo通貨」を使っている国々が、全てxx_flag
NGの場合、それ(それら)がquery_set
に代入されます。「hoge語」と「foo通貨」を使っている国々の中で一つでもOK
の国があれば、それらはいずれも代入されません。「hoge語」と「foo通貨」の国々は全てNGの国なのか!?を判断した上で必要な国のみを抽出しています。