Django使ってる?
PythonのWebフレームワークでお馴染みのDjango。『いつ、どのサイトにどんなツールが導入されたかが分かるSimilarTech』によれば、シェアでは殆どの国でRuby On Railsに劣るものの、トレンドはDjangoが右肩上がりの模様。
さて、本記事の対象者はDjangoをある程度使った事がある人、MTV(あるいはMVC)モデルが多少なりとも分かる人向けの内容となっております。まだ触ったことないよって人は、公式サイトのチュートリアルから始めてみましょう。
順参照と逆参照
まず下記の3つのモデル(鬼殺隊員、階級、呼吸)があるとします。
from django.db import models
class KisatsuMember(models.Model):
""" 鬼殺隊員モデル """
id = models.UUIDField()
# 性 ex) 竈門、我妻、胡蝶
last_name = models.CharField(max_length=20)
# 名 ex) 炭治郎、善逸、しのぶ
first_name = models.CharField(max_length=20)
# 階級
rank = models.ForeignKey(Rank, on_delete=models.PROTECT)
# 呼吸
breath = models.ForeignKey(Breath, on_delete=models.PROTECT, null=True)
# 現役フラグ
is_active = models.BooleanField(default=True)
class Meta:
db_table = 'kisatsu_member'
class Rank(models.Model):
""" 階級モデル """
id = models.UUIDField()
# 階級名 ex) 柱、癸
name = models.CharField(max_length=10)
class Meta:
db_table = 'rank'
class Breath(models.Model):
""" 呼吸モデル """
id = models.UUIDField()
# 呼吸名 ex) 水、雷、獣
name = models.CharField(max_length=10)
# 型の数 ex) 6, 10, 11
number_of_types = models.IntegerField()
class Meta:
db_table = 'breath'
順参照の例
鬼殺隊員から呼吸へ、というように子から親のテーブルの値を参照することを『順参照』と言います。
例として、『水』の呼吸を使う鬼殺隊員の姓・名・型の数を順参照で取得するなら下記のようになります。
※1 後続の処理でループさせる場合はN+1問題の為、select_relatedメソッドでの結合かリスト変換が推奨されます。
water_breath_member = KisatsuMember.objects \
.filter(breath__name="水") \
.values("last_name",
"first_name",
"breath__number_of_types")
print(water_breath_user)
# <QuerySet [{'last_name': '竈門', 'first_name': '炭治郎', 'breath__number_of_types': 10},
# {'last_name': '鱗滝', 'first_name': '左近次', 'breath__number_of_types': 10},
# {'last_name': '冨岡', 'first_name': '義勇', 'breath__number_of_types': 11}]>
発行クエリ
SELECT
kisatsu_member.last_name,
kisatsu_member.first_name,
breath.number_of_types
FROM
kisatsu_member
INNER JOIN
breath
ON kisatsu_member.breath_id = breath.id
WHERE
breath.name = '水'
;
逆参照の例
反対に親から子、階級から鬼殺隊員といった参照のことを『逆参照』と言います。
今度は少し複雑な取得の仕方をしてみます。
階級が『柱』かつ『現役』の鬼殺隊員の『水』以外の呼吸の名前を取得するなら下記のようになります。
逆参照はunderscore抜きのテーブル名を結合させる点が味噌です。
※2 後続の処理でループさせる場合はN+1問題の為、prefetch_relatedメソッドでの結合かリスト変換が推奨されます。
※3 prefetch_related("kisatsumember_set")を使って結合する場合、kisatsumember_set__<カラム名>といった形で結合先の値を扱う事が出来ます。
active_hashira_breathes = Rank.objects \
.filter(name="柱",
kisatsumember__is_active=True) \
.exclude(kisastumember__breath__name="水") \
.values("kisastumember__breath__name")
print(active_hashira_breathes)
# <QuerySet [{'kisastumember__breath__name': '炎'},
# {'kisastumember__breath__name': '恋'},
# {'kisastumember__breath__name': '霞'}...]>
発行クエリ
SELECT
breath.name
FROM
`rank`
INNER JOIN
kisatsu_member
ON kisatsu_member.rank_id = `rank`.id
INNER JOIN
breath
ON kisatsu_member.breath_id = breath.id
WHERE
`rank`.name = '柱'
AND kisatsu_member.is_active = 1
AND breath.name <> '水'
;
※4 Djangoはモデル定義において、外部参照キーがnull=Trueか否かによって、INNER JOINやLEFT OUTER JOIN 等、内部的に判断してSQLに変換していたはずです。(うろ覚え)
一つの道を極めるんじゃ
Pythonしかろくに書けませんが、今後とも精進致します!