スポンサーリンク
概要
SQLAlchemy2.0で、継承によって複数のモデルクラスを作成し、用途に応じて必要なリレーションのフィールドを追加させる方法のメモ。
この方法を使うことで、一つのモデルクラス内に一部のケースでのみ使用するフィールドが増加して肥大化することを防げる。
スポンサーリンク
説明
背景
下記のような書籍レビューモデルと、ユーザーモデルがあるとする。1:1の関係で書籍レビューはユーザーIDのみを保持する。
コメントアウトされたuserフィールドのようにリレーションの実態を持たせ、参照することができる。しかしながら、idだけではなく、UserDTO自体が欲しいケースと欲しくないケースがあるので両方を持たせるのは気持ち悪い。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from sqlalchemy.orm import relationship class BookReviewDTO(Base): __tablename__ = "book_reviews" __table_args__ = {"comment": "書籍レビュー"} review_id: Mapped[UUID] = mapped_column(Uuid, primary_key=True, comment="主キー") content: Mapped[str] = mapped_column(String(length=10000), comment="レビュー本文") user_id: Mapped[UUID] = mapped_column(Uuid, ForeignKey("users.user_id")) # user: Mapped[UserDTO] = relationship() # ユーザモデルの実態も持たせられる。 class UserDTO(DeletableBase): __tablename__ = "users" __table_args__ = {"comment": "ユーザー"} user_id: Mapped[UUID] = mapped_column(Uuid, primary_key=True, comment="主キー") name: Mapped[str] = mapped_column(String(length=30), nullable=False, comment="ユーザー名") |
解決方法
そこで、下記のような元のモデルを継承して、リレーションの実態を持つフィールドを定義する。
1 2 |
class BookReviewExtendDTO(BookReviewDTO): ex_user: Mapped[UserDTO] = relationship() |
下記のようにUserDTOの実態にアクセスしたい場合には、継承したBookReviewExtendDTOでselectを行う。
ベースクラスのBookReviewDTOに側に、使う場合と使わない場合がある、余計なフィールドを定義することを避けることがきて見通しが良くなった。
1 2 3 4 5 6 7 |
stmt = ( select(BookReviewExtendDTO) .options( contains_eager(UserDTO), ) ) results = self._session.scalars(stmt).unique().all() |