SQLAlchemy/モデル間の関係定義
多対多の関係
作品 (artwork) には0個以上のタグ (tag) を付けられる、とする。
artworkとtagとの関連を記録するテーブル (artwork_tag_relation) を作ることで、多対多の関係を保存できる。
ArtworkTagRelation(Base) クラスを作るよりは Table で定義するほうがよい。
ArtworkTagRelation クラスを作ると、作品とタグとの関連を ArtworkTagRelation クラスを用いて表現する必要が生じてしまい、冗長な書き方になるし、実装が透けて見える。また、GraphQL APIを作る際にも ArtworkTagRelation 型のフィールドを明示的に操作することになってしまい、ぎこちなくなる。
Table でテーブルを作るにとどめれば、作品とタグとの関連は artwork.tags フィールドに付けたいタグを足すとか、tag.artworks フィールドでタグが付いた作品を列挙するとか、直感的になる。
artwork.tags に cascade="all, delete" を指定すると、作品を削除した際にタグごと削除してしまうので、指定しない。
code:model_artwork_tag.py
Base = declarative_base()
# 関連テーブル
artwork_tag_relation = Table(
"artwork_tag_relation",
Base.metadata,
Column("artwork_id", Integer, ForeignKey("artwork.id")),
Column("tag_id", Integer, ForeignKey("tag.id")),
)
class Artwork(Base):
__tablename__ = "artwork"
id = Column(Integer, primary_key=True, autoincrement=True)
...
tags = relationship(
"Tag",
secondary=artwork_tag_relation,
back_populates="artworks",
)
class Tag(Base):
__tablename__ = "tag"
id = Column(Integer, primary_key=True, autoincrement=True)
...
artworks = relationship(
"Artwork", secondary=artwork_tag_relation, back_populates="tags"
)
エイリアス的に指定された関係
作品 (artwork) は複数のイラスト (illust) を持つものとする。ここで、作品を代表するイラストを artwork.top_illust というフィールドで参照できるようにしたい。
作品を代表するイラストのidを保持するカラム artwork.top_illust_id を作り、そのidでjoinするような関係を定義すればよい。
post_update=True を指定しないと循環参照が検出されたようなエラーで落ちるので、あった方がよさそう。
code:model_artwork_top_illust.py
class Artwork(Base):
__tablename__ = "artwork"
id = Column(Integer, primary_key=True, autoincrement=True)
top_illust_id = Column(Integer, ForeignKey("illust.id"))
# 作品を代表するイラスト
top_illust = relationship(
"Illust",
primaryjoin="Artwork.top_illust_id == Illust.id",
foreign_keys=top_illust_id,
post_update=True,
)
# 作品に属するイラスト
illusts = relationship("Illust", backref="artwork", cascade="all, delete")
class Illust(Base):
__tablename__ = "illust"
id = Column(Integer, primary_key=True, autoincrement=True)
artwork_id = Column(Integer, ForeignKey("artwork.id"), nullable=False)