初めに
Djangoに携わっている人は、親子関係にあるテーブルの情報を扱う際に、特定の親テーブルの情報を持つ子テーブルのみ取得するということがよくあると思います。(後で具体例を用いて説明します)
その時の手法としてFilterとSelect_relatedどちらかを使うでしょうか?
明確に使い所を切り分けて使っている方はこれより先読まなくても大丈夫です(笑)
それ以外の方!
filterじゃダメなのか、どういう状況でselect_relatedを使うのか、紹介できればと思います。
まず今回使うテーブルのモデル定義をしていきます。
モデル定義
今回はJリーグのサッカーチームとそれに所属する選手のモデルを作成しました。
from django.db import models
class Team(models.Model):
name = models.CharField(max_length=20)
# J1、J2のような所属ランク、本来ならマスターテーブルで管理するが、今回は例なので文字列で管理
rank = models.CharField(max_length=20)
class Member(models.Model):
team = models.ForeignKey('Team', on_delete=models.CASCADE,)
name = models.CharField(max_length=20)
age = models.PositiveSmallIntegerField()
salary = models.IntegerField()
上記のようなモデルがある時、以下の場合はどのように情報を取得しますか?
取得したい情報
- J1に所属している選手のみ取得したい場合
- J2に所属している選手とそのチーム名を取得したい場合
それぞれfilterとselect_relatedどちらを使うでしょうか?(考えてみてください。)
取得したい情報①
J1に所属している選手のみ取得したい場合
qs = Member.objects.filter(team__rank="J1")
for qs_obj in qs:
print(qs_obj.name)
実際にコンソール出力してみると、、、
>>> for qs_obj in qs:
... print(qs_obj.name)
...
大迫 勇也
山口 蛍
宮市 亮
杉本 健勇
>>>
こんな感じでJ1に所属している選手を取得できました!
取得したい情報②
では、次に
J2に所属している選手とそのチーム名を取得したい場合
qs = Member.objects.select_related("team").filter(team__rank="J2")
for qs_obj in qs:
print(f'{qs_obj.team.rank}所属:{qs_obj.team.name} 氏名:{qs_obj.name}')
実際にコンソール出力してみると、、、
>>> for qs_obj in qs:
... print(f'{qs_obj.team.rank}所属:{qs_obj.team.name} 氏名:{qs_obj.name}')
...
J2所属:FC町田ゼルビア 氏名:アデミウソン
J2所属:FC町田ゼルビア 氏名:髙橋大悟
J2所属:清水エスパルス 氏名:権田修一
J2所属:清水エスパルス 氏名:乾貴士
こんな感じになります。
違いは?
「filterとselect_relatedの使いどころの違いは、親要素の情報も取得したいかどうか」という点にあると思います。
つまり、取得したい情報①と情報②は一見同じクエリセットですが、情報①で取得したクエリセットはTeamオブジェクトを含みません。つまり、J1のチームに所属する選手は取得できても、J1のチーム名を取得したいとなったら、再度SQLが発行されてしまうということが起こります。
一方select_relatedであれば、親要素をオブジェクトとして持つので、そのようなことは起こりません。
よって、「親要素の情報も取得したいかどうか」によってFilterか select_relatedを使い分けると良いです!