LoginSignup
16
19

More than 3 years have passed since last update.

Djangoのモデル:ManyToManyField

Last updated at Posted at 2019-11-06

モデルの例と定義

サッカー選手(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をしたら、下記の三つテーブルが作成されます。
image.png

player_positionというテーブルは、playerテーブルのPKとpositionテーブルのPKで、以下のように情報を管理しています。
image.png

検索の例

「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
16
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
19