今回のお題
今回はdjangoアプリに多数対多数のリレーションを実装していきます。
1対多数のリレーションについては以下をご参照ください。
目次
- models.py
- views.py, テンプレート
- おまけ〜中間テーブル
- 終わりに
models.py
1対多数の場合と同じく、models.pyにリレーションを記述していきます。
なお今回は、メンバーを何人か選択してチャットルームを作成するアプリを想定します。
チャットルームには複数のメンバーが参加していますし、逆に同一のメンバーが複数のチャットルームに参加することもあり得るので、多数対多数の結びつきになります。
class Member(models.Model):
name = models.CharField(verbose_name="名前", max_length=30)
class Room(models.Model):
name = models.CharField(verbose_name="ルーム名", max_length=20)
members = models.ManyToManyField(Member)
基本的には
"フィールド名" = models.ManyToManyField(対象のモデル)
と記述するだけでOKです。
views.py,テンプレート
リレーション先の取得方法は基本的に1対多数の時と同じです。
多数対多数であっても親子の区別はあることに注意してください。
今回はmembers
というフィールドを設定したRoomモデルが子供側になります。
# 親側はxxx_set.allで取得
rooms = member.room_set.all
# 子供側には_setは不要
members = room.members.all
おまけ〜中間テーブル
他の言語のフレームワークと同じように、djangoでも多数対多数のリレーションは中間テーブルで管理されています。
ただし自分で定義するのではなく、上記のManyToManyField
を定義してマイグレーションした時点で自動で生成されるようです。
なので例えば上記のRoomモデルのテーブルであればmembersというカラムがDBに存在するわけではなく、実際には以下のようなテーブルで両者のリレーションが管理されているようです(たまたま手元にあったコードのコピペなので、上記とはモデル設定が異なります。少しややこしいですが、author
をroom
、book
をmember
と読み替えると整合性が取れるようになります)。
sqlite> .schema main_author
CREATE TABLE IF NOT EXISTS "main_author" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(20) NOT NULL, "address" varchar(100) NOT NULL);
sqlite> .schema main_author_books
CREATE TABLE IF NOT EXISTS "main_author_books" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "author_id" bigint NOT NULL REFERENCES "main_author" ("id") DEFERRABLE INITIALLY DEFERRED, "book_id" bigint NOT NULL REFERENCES "main_book" ("id") DEFERRABLE INITIALLY DEFERRED);
CREATE UNIQUE INDEX "main_author_books_author_id_book_id_6fa9303d_uniq" ON "main_author_books" ("author_id", "book_id");
CREATE INDEX "main_author_books_author_id_fe7fb621" ON "main_author_books" ("author_id");
CREATE INDEX "main_author_books_book_id_e205b261" ON "main_author_books" ("book_id");
後半のINDEXの部分に関する説明は省略しますが、
- 子供側のテーブルにも親側を参照するカラムは作られない
- その代わりに中間テーブルが作られ、多数対多数のリレーションが管理される。
ということをご認識いただければ大丈夫かと思います。
終わりに
以上でdjangoアプリに多数対多数のリレーションが実装できました。
中間テーブルが勝手に作られるので、便利と言えば便利ですね。
ただ、その分内部でどのような処理になっているのかがブラックボックスになりやすいので、そこはしっかりと押さえていきたいと思います。