ヒトリ歩き

愚痴とかいろいろ書きます

SQLAlchemyでのJoinで結合したデータを取得

前回、SELECT 〜 JOIN をやってみたが、期待通りのことが出来なかった。 少し調べたら、やり方がわかったのでまとめておく。

kotapontan.hatenablog.com

selectメソッドに対象のモデルを指定する

selectメソッドで検索時に取得するデータのモデルを指定して、executeメソッドを実行するだけ。 scalarsメソッドは特定のカラムの値を抽出するために使用するため、scalarsメソッドでデータ取得するとStudentテーブルのデータが取得できない。 executeメソッドは、全体の結果を取得するため、結合して取得したデータに対して、対応するモデルに値を設定してくれる。

stmt = select(School, Student).join(School, Student.school_id == School.id)

results = session.execute(stmt)

値の参照方法

以下のように、Rowインスタンスモデルクラス.変数名でアクセスできる。 Studentテーブルのnameカラムの値を取得したい場合、Rowインスタンスの変数名.Student.nameで取得する。

    print("student.id = " + str(result.Student.id))
    print("student.name = " + str(result.Student.name))
    print("student.school.name = " + str(result.School.name))

最後に

SQLAlchemyのマニュアルに複数のORMエンティティを使用する場合について記載されていた。やりたいことがマニュアルに書かれていたので、改めてマニュアルを確認することは大事だと気付かされた。

参考

docs.sqlalchemy.org

docs.sqlalchemy.org

docs.sqlalchemy.org

ソース

from sqlalchemy import create_engine
from sqlalchemy.types import Integer, String
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import relationship
from sqlalchemy import ForeignKey
from sqlalchemy import select


class Base(DeclarativeBase):
    pass


class Student(Base):
    __tablename__ = "student"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(30))
    school_id: Mapped[int] = mapped_column(ForeignKey("school.id"))


class School(Base):
    __tablename__ = "school"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(30))


# DBエンジンを作成
url = "postgresql://postgres:example@localhost:5432/postgres"
engine = create_engine(url, echo=True)

# テーブルをDBに作成
Base.metadata.create_all(engine)

# セッションを作成
# セッションを介してクエリを実行する
SessionClass = sessionmaker(engine)
session = SessionClass()

school1 = School(name="あいうえお学園")
school2 = School(name="かきくけこ学園")
school3 = School(name="さしすせそ学校")
session.add_all([school1, school2, school3])
session.commit()
student1 = Student(name="sato", school_id=1)
student2 = Student(name="tanaka", school_id=2)
student3 = Student(name="watanabe", school_id=3)
session.add_all([student1, student2, student3])
session.commit()

stmt = select(School, Student).join(School, Student.school_id == School.id)


# results = session.scalars(stmt)
results = session.execute(stmt)

for result in results:
    print("student.id = " + str(result.Student.id))
    print("student.name = " + str(result.Student.name))
    print("student.school.name = " + str(result.School.name))
version: "3.1"

services:
  db:
    image: postgres
    restart: always
    environment:
      POSTGRES_PASSWORD: example
    ports:
      - 5432:5432

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080