モデルの例と定義
サッカー選手(Player)とそのポジション(Position)を例として考えて見ます。
1、選手は複数のポジションができる
2、同じポジションの選手は複数存在
この場合はManyToManyの出番です。
モデルの例
class Position(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=20, unique=True)
class Player(models.Model):
・・・(略)・・・
position = models.ManyToManyField(Position)
上記のモデル定義を用いてDjangoのmigrateをしたら、下記の三つテーブルが作成されます。
player_positionというテーブルは、playerテーブルのPKとpositionテーブルのPKで、以下のように情報を管理しています。
検索の例
「Pirlo」という選手のポジションをすべて抽出
positions = Player.objects.get(name='Pirlo').position.all()
[print(p.name) for p in positions]
実行結果:
DM
CM
「CB」というポジションの選手をすべて抽出(方法1)
players = Player.objects.filter(position=Position.objects.get(name='CB'))
[print(p.name) for p in players]
実行結果:
Maldini
Nesta
「CB」というポジションの選手をすべて抽出(方法2)
モデルの例(方法2)
class Player(models.Model):
・・・(略)・・・
position = models.ManyToManyField(Position, related_name='player')
※選手モデルののポジション定義に、上記のように「related_name='player'」という逆参照を追加すれば、下記のようにポジションの実行結果から選手を直接取得することも可能
(migration不要)
players = Position.objects.get(name='CB').player.all()
[print(p.name) for p in players]
実行結果:
Maldini
Nesta
追記
「CB」というポジションの選手をすべて抽出した方法1と方法2ですが、作成されたクエリを確認したところ、両方も下記のSQLを生成しました。
SELECT "soccer_player"."id", "soccer_player"."name", "soccer_player"."team_id"
FROM "soccer_player"
INNER JOIN "soccer_player_position" ON ("soccer_player"."id" = "soccer_player_position"."player_id")
WHERE "soccer_player_position"."position_id" = 1