SQLAlchemy入門 ORM その4 リレーションとJOIN

前回、テーブル間のリレーションを考慮しないJOINについて学習しました。今回はテーブルクラスにリレーションを定義する方法とそれらが考慮されたJOINについて学習しましょう。

relationshipとjoin

あるテーブルのレコードに対して、別のテーブルのレコードが複数紐づくようなリレーションです。例えば、ブログシステムにおけるユーザーと投稿の関係などがこれに該当しますね。子テーブルに外部キーを定義し、親テーブルクラス内で以下のようにrelationship関数を使用します。

子属性 = relationship("子テーブル")

前回のサンプルにOne To Manyのリレーション定義を追加したサンプルを見てみましょう。

from sqlalchemy import Table, Column, Integer, ForeignKey, String
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# DB接続
engine = create_engine('sqlite:///:memory:')

Base = declarative_base()


class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    age = Column(Integer)

    posts = relationship("Post", backref="users")    # Postとのリレーション


class Post(Base):
    __tablename__ = 'posts'
    id = Column(Integer, primary_key=True)
    users_id = Column(Integer, ForeignKey('users.id'))
    title = Column(String)
    body = Column(Integer)

    user = relationship("User")    # Userとのリレーション

# テーブルクラスのテーブルを生成
Base.metadata.create_all(engine)

# セッション生成
Session = sessionmaker(bind=engine)
session = Session()

# サンプルデータ挿入
session.add(User(id=1, name="Suzuki", age=19))
session.add(User(id=2, name="Tanaka", age=21))
session.add(User(id=3, name="Sato", age=21))

session.add(Post(users_id=1, title="朝の体操", body="ラジオ体操で元気いっぱい"))
session.add(Post(users_id=1, title="今日の夕食", body="カレーラスがとても美味しかった。"))
session.add(Post(users_id=2, title="仕事", body="今日はDjangoでAPI作成。"))
session.add(Post(users_id=2, title="Python楽しい", body="Python楽しいですよね!!"))
session.commit()

# inner joinのサンプル
users = session.query(User).join(Post, User.id == Post.users_id).all()

for user in users:
    print("%sさんの投稿" % (user.name))
    for post in user.posts:
        print("|- タイトル:%s" % (post.title,))
    print('')

以下のような実行結果が得られます。前回のサンプルと異なり、結果オブジェクトが階層化されていることが確認できます。

Suzukiさんの投稿
|- タイトル:朝の体操
|- タイトル:今日の夕食

Tanakaさんの投稿
|- タイトル:仕事
|- タイトル:Python楽しい

また、relationship関数の引数にback_populates、backref、uselistなどの引数を指定することにより、関係の双方向性やユニーク性を定義することができます。これらの組み合わせによりOne To Many、Many To One、One To One、Many To Many、アソシエーションといった関係を定義することができます。