カテゴリ: Flask 更新日: 2026/01/06

Flaskで多対多のリレーションを作る!中間テーブルの基本構造を学ぼう

Flaskで多対多のリレーションを作る!中間テーブルの基本構造を学ぼう
Flaskで多対多のリレーションを作る!中間テーブルの基本構造を学ぼう

先生と生徒の会話形式で理解しよう

生徒

「Flaskでたくさんのデータ同士をつなげたいときはどうすればいいですか?」

先生

「その場合は『多対多リレーション』と呼ばれる関係を設定します。多対多って聞くと難しそうに思えますが、わかりやすく学びましょう!」

生徒

「多対多ってどういう意味ですか?イメージしにくいです…」

先生

「簡単に言うと、例えば『記事とタグ』の関係です。1つの記事に複数のタグ、1つのタグが複数の記事に使われるような関係です」

1. 多対多リレーションとは?

1. 多対多リレーションとは?
1. 多対多リレーションとは?

多対多リレーションとは、「Aのデータが複数のBとつながり、Bも複数のAとつながる」という関係のことです。たとえば、1人の学生が複数の授業を受けられ、同じ授業を複数の学生が受けるような状況がこれに当たります。現実の場面と似ているので、一度イメージがつけば理解しやすくなります。

しかし、この関係はそのままではデータベースに保存できません。そこで必要になるのが中間テーブルです。中間テーブルは、AとBを直接つなぐのではなく「AのID」と「BのID」だけを管理する“専用の橋渡し役”のようなものです。これによって、複雑なつながりを無理なく整理できるようになります。

イメージを掴むために、紙のカードを使った簡単な例を考えてみましょう。学生カードと授業カードを並べ、その間に「参加する」というカードを置いて線で結ぶと、どの学生がどの授業を受けているのかひと目で分かります。この「線の情報」を保存するのが中間テーブルだと言えます。


学生 ── 参加テーブル ── 授業
  A      Aと数学を結ぶ行
         Aと英語を結ぶ行
  B      Bと数学を結ぶ行

このように、多対多は最初こそ複雑に見えますが、「間に1枚のメモ帳(中間テーブル)を置いて関係だけを記録する」と考えるだけで理解しやすくなります。この仕組みを使うことで、Flaskでも柔軟なデータ管理ができるようになります。

2. FlaskとSQLAlchemyで多対多を設定する準備

2. FlaskとSQLAlchemyで多対多を設定する準備
2. FlaskとSQLAlchemyで多対多を設定する準備

多対多リレーションをFlaskで扱うためには、まず「Flaskアプリ」と「SQLAlchemy(データベースを扱うためのライブラリ)」が動く土台を用意しておく必要があります。ここでは、まだ中間テーブルやモデルの細かいコードには入らず、その一歩手前の“スタート地点”を整えるイメージで準備をしていきます。

はじめに、作業用のフォルダを1つ用意し、その中にapp.pyのようなPythonファイルを1つ作ります。次に、ターミナルやコマンドプロンプトを開き、FlaskとSQLAlchemyをインストールしておきましょう。


pip install flask flask_sqlalchemy

インストールができたら、Flaskアプリとデータベース接続の“ひな形”となるコードを書いていきます。多対多リレーションの学習では何度も登場する基本形なので、ここで一度しっかり押さえておくと後がとても楽になります。


from flask import Flask
from flask_sqlalchemy import SQLAlchemy

# Flaskアプリ本体を作成
app = Flask(__name__)

# どのデータベースを使うかを設定(ここではSQLiteを利用)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///example.db'

# Flaskとデータベースをつなぐオブジェクト
db = SQLAlchemy(app)

app = Flask(__name__)は「これからFlaskアプリを動かします」という宣言で、SQLALCHEMY_DATABASE_URIには「どのデータベースを使うか」を指定しています。ここでは、ファイル1つで扱えるSQLiteを使っており、小さなサンプルや多対多リレーションの学習にはぴったりです。db = SQLAlchemy(app)で、FlaskアプリとSQLAlchemyが連携し、このdbを使って後ほどモデルや中間テーブルを定義していくことになります。

ここまで準備ができていれば、FlaskとSQLAlchemyを使った多対多リレーションの土台は整いました。次のステップでは、この土台の上に中間テーブルを定義して、実際にデータ同士のつながりを表現していきます。

3. 中間テーブルを定義してみよう

3. 中間テーブルを定義してみよう
3. 中間テーブルを定義してみよう

次に、実際のPythonコードで中間テーブルを定義してみましょう。ここでは、ブログの記事を表すpostsと、記事につけるtags(タグ)の関係を例にします。「1つの記事に複数のタグ」「1つのタグが複数の記事に使われる」という、典型的な多対多リレーションです。

SQLAlchemyでは、中間テーブル専用のモデルクラスを作るのではなく、db.Tableという形で“テーブルだけ”を定義する書き方がよく使われます。まずはコードを見て雰囲気をつかみましょう。


from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

# 記事(Post)とタグ(Tag)をつなぐ中間テーブル
post_tags = db.Table(
    'post_tags',
    db.Column('post_id', db.Integer, db.ForeignKey('post.id'), primary_key=True),
    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'), primary_key=True)
)

このpost_tagsが中間テーブルを表すオブジェクトです。実際のデータベース上では「post_tags」というテーブルになり、post_idtag_idという2つの列だけを持ちます。それぞれの記事ID・タグIDが1行ずつ保存され、「どの記事とどのタグがつながっているのか」という情報だけをシンプルに記録します。

db.ForeignKey('post.id')は「postテーブルのid列と関連しています」という意味で、db.ForeignKey('tag.id')は「tagテーブルのid列と関連しています」という指定です。どちらもprimary_key=Trueを付けているのは、「同じ記事と同じタグの組み合わせを重複して登録しないようにする」ためのルールと考えておくとイメージしやすいでしょう。

イメージとしては、次のような表をコードで用意しているのと同じです。


post_tags テーブル(中間テーブルのイメージ)

post_id   tag_id
-----------------
1         1   ← 記事1にタグ1が付いている
1         2   ← 記事1にタグ2が付いている
2         1   ← 記事2にタグ1が付いている

このように、中間テーブルは「記事そのものの内容」や「タグの名前」ではなく、「どの組み合わせでつながっているか」だけを管理します。FlaskとSQLAlchemyでは、このpost_tagsを土台にして、次のステップでPostモデルやTagモデルから多対多リレーションを簡単に扱えるようにしていきます。

4. PostとTagのモデルに関係を追加

4. PostとTagのモデルに関係を追加
4. PostとTagのモデルに関係を追加

中間テーブルを定義できたら、次は実際に記事(Post)とタグ(Tag)がどのようにつながるのかをモデル側で表現していきます。ここを理解すると、多対多リレーションの動きが一気に分かりやすくなり、データの登録や取得もスムーズに行えるようになります。

下のコードは、記事とタグが「お互いに複数と結びつけられる」という関係を示す最小構成のサンプルです。まずは全体を眺めて流れをつかんでみましょう。


class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)

    # 記事が持つタグの一覧を表すリレーション
    tags = db.relationship(
        'Tag',
        secondary=post_tags,      # 中間テーブルを使う
        back_populates='posts'    # Tag 側との連動
    )

class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(30), unique=True, nullable=False)

    # あるタグが付けられた記事一覧を取得するための設定
    posts = db.relationship(
        'Post',
        secondary=post_tags,      # 同じ中間テーブルを利用
        back_populates='tags'     # Post 側との連動
    )

tags = db.relationship('Tag', secondary=post_tags)の部分が、記事モデルに「タグの一覧」を持たせている箇所です。ここでsecondaryに中間テーブルを指定することで、「記事 ⇔ 中間テーブル ⇔ タグ」という流れがデータベース上で自然につながります。

一方、back_populatesは「反対側のモデルでも同じ関係を辿れるようにするための設定」です。これがあるおかげで、post.tagsでタグ一覧を取れたり、tag.postsで記事一覧を取れたりと、双方向にスムーズにアクセスできます。

よりイメージしやすくするために、小さな図にすると次のような関係です。


Post(記事) ←→ post_tags(中間) ←→ Tag(タグ)

Post.tags でタグ一覧が見られる
Tag.posts でそのタグが付いた記事一覧が見られる

この設定を行った時点で、記事とタグの関係性はSQLAlchemyにしっかり理解され、あとはデータを追加するだけで自然と多対多のつながりが構築されていきます。複雑に見えても、コードとしてはとてもシンプルなので、仕組みを一度覚えてしまえば他のモデルでも応用しやすくなります。

5. データを追加してリレーションを見る

5. データを追加してリレーションを見る
5. データを追加してリレーションを見る

モデルで多対多の関係を設定できたら、次は実際にデータを追加してそのつながりがどう反映されるのかを確認してみましょう。ここでは、記事(Post)とタグ(Tag)をいくつか作り、記事にタグを紐づける流れをゆっくりと見ていきます。初心者の方でも動きをイメージしやすいよう、できるだけシンプルな形で紹介します。


# 新しい記事を1つ作成
post = Post(title="Flask入門")

# 記事につけるタグを複数作成
t1 = Tag(name="Python")
t2 = Tag(name="Web")

# 記事にタグを関連付ける(多対多リレーションのポイント)
post.tags.append(t1)
post.tags.append(t2)

# データベースに追加して保存する
db.session.add(post)
db.session.commit()

ここで行っていることはとてもシンプルです。まず記事を作り、次にタグを作成し、その後 post.tags.append() で「この記事にはこのタグが付いています」という情報を追加しています。この一行が実際には中間テーブルにデータを1行ずつ追加しており、記事とタグのつながりが裏側で管理されていきます。

例えば、上記のコードを実行すると「Flask入門」という記事には「Python」と「Web」という2つのタグが紐づけられます。これは、“記事1にタグAとタグBがつながった”という情報が中間テーブルに登録された状態だと考えるとイメージしやすいでしょう。


post_tags テーブルに入るイメージ

post_id   tag_id
-----------------
1         1   ← Flask入門 × Python
1         2   ← Flask入門 × Web

このように、中間テーブルを意識せずとも「appendでタグをつけるだけ」で多対多の関係が構築されます。初心者でも扱いやすく、実際のアプリ開発でも非常によく使われる操作なので、まずは小さなサンプルから試して感覚をつかんでみてください。

6. 関係を使ってデータを取得してみよう

6. 関係を使ってデータを取得してみよう
6. 関係を使ってデータを取得してみよう

中間テーブルのおかげで、簡単に関連する情報を取り出せます。


p = Post.query.first()
for tag in p.tags:
    print(tag.name)

t = Tag.query.filter_by(name="Python").first()
for post in t.posts:
    print(post.title)

Python
Web
Flask入門

記事からタグ、またタグから記事が参照できています。

7. 多対多のリレーションで気をつけるポイント

7. 多対多のリレーションで気をつけるポイント
7. 多対多のリレーションで気をつけるポイント
  • 中間テーブル名、モデル名、ForeignKeyの参照が正しいか確認
  • secondaryback_populatesを間違えないように
  • モデル追加後はdb.create_all()やマイグレーションを忘れずに

8. 多対多はどんな場面で使う?

8. 多対多はどんな場面で使う?
8. 多対多はどんな場面で使う?

多対多のリレーションは、

  • 記事とタグ
  • ユーザーと役割権限
  • 商品とカテゴリー

といった「どちらも複数の関係を持つ」ケースでとても役立ちます。

まとめ

まとめ
まとめ

Flaskで多対多のリレーションを扱うための中間テーブルについてじっくり見てきましたが、全体を振り返ってみると、複雑に見えるデータ同士のつながりも、きちんと整理された構造と仕組みを理解すれば迷わず扱えるようになることがよく分かります。とくに多対多は初心者がつまずきやすい話題ですが、実際には「中間テーブルという橋渡しがあるだけ」と捉えることでとても理解しやすくなります。今回の記事では、記事とタグ、ユーザーと役割、商品とカテゴリーといった、日常的なアプリケーションで頻繁に使われる関係を例に挙げて実際のコードとともに学習したことで、より現実的な用途をイメージしながら学べたのではないでしょうか。データベースのリレーションを理解することは、FlaskやSQLAlchemyを使った開発において欠かせない基礎力となりますし、複雑なアプリケーションを設計する際の大切な判断材料にもつながります。

また今回扱った中間テーブルの定義は、FlaskとSQLAlchemyの組み合わせで多対多リレーションを実現する際の基本形ともいえる書き方でした。とくにdb.Tableで中間テーブルを定義し、secondaryでそのテーブルをモデル同士に関連づける流れは、多対多の定番パターンとして覚えておくと応用が効きます。さらにback_populatesによって、片方のモデルからもう片方のモデルへ、またその逆方向にも自然にアクセスできるようになり、複雑な検索やデータ操作がぐっとやりやすくなります。こうした仕組みを理解しておくと、アプリケーションに柔軟性を持たせることができ、データ構造の拡張にも安心して取り組めるようになります。実際の場面では新しい要件が追加され、データ同士の関係を見直すことも少なくありません。そのたびに土台として役に立つのが今回学んだ多対多の概念です。

さらに、モデルにデータを追加するときのコード例を確認したことで、実際に多対多がどう動くのかを具体的にイメージできたと思います。記事に複数のタグを追加したり、タグ側からそのタグを持つ記事を一覧で取得したりする操作は、どのアプリケーションでも頻繁に利用される重要な機能です。中間テーブルが正しく機能していることで、こうした関連データの取得がとても簡単になる点は、SQLAlchemyの便利さを感じる部分でもあります。複雑なSQL文を自分で書かなくても、Pythonのコードだけで直感的に操作できるのは非常に大きな利点です。

そして設計面で考えておきたいのが、多対多リレーションを使うときに注意するべきポイントです。中間テーブルの名前、外部キーの参照先、さらにモデル同士の関連づけが正しく指定されているかどうかは、動作に直結する大切な部分です。また、モデルの定義を追加・変更したときにはテーブルを作り直したりマイグレーションを行う必要があることも忘れてはいけません。このあたりを丁寧に進めることで、後から原因不明のエラーに悩まされることが少なくなります。アプリケーションを長く運用する場合や、複数人で作業する開発環境では、こうした整った基盤ほど重宝します。

ここで改めて、今回学んだリレーションの仕組みを使ったサンプルコードをもう一度示しておきます。多対多の基礎を確認する小さなコードですが、中間テーブルの役割やモデル同士のつながりを再確認する助けになるはずです。

サンプルコードの振り返り


from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

post_tags = db.Table('post_tags',
    db.Column('post_id', db.Integer, db.ForeignKey('post.id'), primary_key=True),
    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'), primary_key=True)
)

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    tags = db.relationship('Tag', secondary=post_tags,
                           back_populates='posts')

class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(30), unique=True, nullable=False)
    posts = db.relationship('Post', secondary=post_tags,
                            back_populates='tags')

# データ追加例
post = Post(title="多対多の学習")
t1 = Tag(name="Python")
t2 = Tag(name="Flask")
post.tags.append(t1)
post.tags.append(t2)

db.session.add(post)
db.session.commit()

こうしたコードを自分の手で書きながら動作を確かめていくことで、多対多に対する理解はぐっと深まります。小さな例でもいいので、実際にデータを登録したり取得してみたりすることで感覚がつかめるようになります。データベース設計は一度理解すると非常に応用が利くため、今回の学びは確かな財産になっていくはずです。

先生と生徒の振り返り会話

生徒

「中間テーブルって最初は難しく感じましたけど、実際には『リストみたいな表』で記事とタグを結びつけていただけなんですね。」

先生

「そうなんです。多対多は構造を理解すれば一気にわかりやすくなりますよ。FlaskとSQLAlchemyの組み合わせなら定義も比較的シンプルです。」

生徒

secondaryとかback_populatesの意味もだいぶ分かってきました。関連するデータを取り出すときにすごく便利ですね。」

先生

「そのとおり。データ同士のつながりを自然に扱えるのが多対多リレーションの強みです。現場でもたくさん使われるので、今回の理解は必ず役に立ちますよ。」

生徒

「これなら自分でもアプリでタグ機能やカテゴリー管理を作れそうです!」

先生

「ぜひ挑戦してみてください。実際に作ることで、さらに理解が深まりますよ。」

この記事を読んだ人からの質問

この記事を読んだ人からの質問
この記事を読んだ人からの質問

プログラミング初心者からのよくある疑問/質問を解決します

Flaskで多対多リレーションとは何ですか?初心者にもわかりやすく教えてください。

多対多リレーションとは、例えば「1つの記事に複数のタグ」「1つのタグが複数の記事に使われる」ような関係のことです。Flaskでは中間テーブルを使ってこの関係を表現します。
カテゴリの一覧へ
新着記事
New1
Flask
Flaskでデータベースエラーを処理する方法!初心者にもわかる例外の使い方
New2
Flask
FlaskでリダイレクトやURL生成を行う方法!便利な関数の使い方を解説
New3
Flask
FlaskでPOSTリクエストを受け取る方法!初心者でもわかるJSONデータの受け取り方
New4
Flask
Flask‑Mailの使い方!アプリからメールを送信する基本方法を解説
人気記事
No.1
Java&Spring記事人気No1
Django
Django環境構築の全手順を完全解説!初心者でも迷わないPython・Djangoセットアップガイド
No.2
Java&Spring記事人気No2
Django
DjangoとFlaskの違いを完全比較!初心者でもわかるPythonフレームワーク入門
No.3
Java&Spring記事人気No3
Python
Pythonでリストをコピーする方法!copy()・スライス・list()の使い方を比較
No.4
Java&Spring記事人気No4
Python
Pythonの文字列を1文字ずつ処理する方法!for文やlist化の活用例
No.5
Java&Spring記事人気No5
Flask
FlaskアプリをNginx + Gunicornで本番運用する方法!初心者でもわかるデプロイ構成の基本
No.6
Java&Spring記事人気No6
Python
PythonでHello Worldを表示するには?初心者向けに最初の1行を実行してみよう
No.7
Java&Spring記事人気No7
Flask
Flaskとは何か?初心者向けにできること・特徴・インストール手順までやさしく解説
No.8
Java&Spring記事人気No8
Python
Pythonのmatch文(パターンマッチング)とは?switch文の代替としての使い方をやさしく解説