概要
Mapped カラムを使うとより Python のデータクラスっぽくモデルを定義することができます
また declarative_base の使い方も変わったので v2 にあった記載方法を紹介します
環境
- macOS 11.7.10
- Python 3.11.6
- sqlalchemy 2.0.25
サンプルコード
from sqlalchemy import create_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column
from sqlalchemy.types import JSON
engine = create_engine("mysql+pymysql://root@localhost/test?charset=utf8mb4")
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = 'user'
id: Mapped[int] = mapped_column(primary_key=True)
profile: Mapped[dict] = mapped_column(JSON())
class UserTable():
def __init__(self, session: Session):
self.session = session
def select_all(self):
return self.session.query(User).all()
if __name__ == '__main__':
with Session(engine) as session:
user_table = UserTable(session)
records = user_table.select_all()
for r in records:
print(r.id)
print(r.profile)
解説
DeclarativeBase は直接使用できないの必ず DeclarativeBase を継承したベースクラスを作成してそのベースクラスを元にモデルを定義する必要があります
モデルは Mapped と一緒に型を使ってタイプヒトっぽいく記載します
そしてカラムのオプション情報などを mapped_column を使って定義します
単純な文字列を管理するクラスであれば mapped_column を使ったオプション情報は不要です
session も session_maker からは生成せずに直接クラスに engine 情報を渡すことで生成できます
基本は with セッションを使えば自動でクローズしてくれるので with と併用しましょう
JSON を TypedDict に自動バインドする
sqlalchemy v2 では json は dict として扱うので受け取るときに TypedDict として受け取ることもできます
できれば dataclass などのクラスに変換してほしかったのですが単純にやってみたところダメそうだったので自力でやるか他の方法があるのかもしれません
from typing import TypedDict
from sqlalchemy import create_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column
from sqlalchemy.types import JSON
engine = create_engine("mysql+pymysql://root@localhost/test?charset=utf8mb4")
class Profile(TypedDict):
name: str
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = 'user'
id: Mapped[int] = mapped_column(primary_key=True)
profile: Mapped[Profile] = mapped_column(JSON())
class UserTable():
def __init__(self, session: Session):
self.session = session
def select_all(self):
return self.session.query(User).all()
if __name__ == '__main__':
with Session(engine) as session:
user_table = UserTable(session)
records = user_table.select_all()
for r in records:
print(r.id)
print(r.profile["name"])
参考: v1 でのサンプルコード
ライブラリが v2 でも互換があるので動作しますがそのうち使えなくなるかもしれないです
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, sessionmaker
from sqlalchemy.schema import Column
from sqlalchemy.types import JSON, String
engine = create_engine("mysql+pymysql://root@localhost/test?charset=utf8mb4")
SessionClass = sessionmaker(engine)
db_session = SessionClass()
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(String(32), primary_key=True)
profile = Column(JSON())
class UserTable():
def select_all(self):
return db_session.query(User).all()
if __name__ == '__main__':
user_table = UserTable()
records = user_table.select_all()
for r in records:
print(r.id)
print(r.profile)
最後に
SQLAlchemy v2 では Mapped を使ってモデルをタイプヒントっぽく定義できるようになっています
JSON の扱い方はもう少し検討が必要そうです
0 件のコメント:
コメントを投稿