SQLModelとは?
SQLModelは、PydanticとSQLAlchemyを組み合わせた最新のPython ORMライブラリです。 型ヒントを活用したシンプルなコードで、データベース操作を安全かつ効率的に実装できます。
基本操作
インストール
pip install sqlmodel
SQLAlchemy、Pydanticも自動でインストールされます
モデル定義
from sqlmodel import SQLModel, Field
from typing import Optional
class User(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
email: str
age: Optional[int] = None
Pythonのクラスでテーブル構造を定義
データベース/テーブル作成
from sqlmodel import create_engine
# エンジン作成
engine = create_engine("sqlite:///database.db")
# すべてのテーブルを作成
SQLModel.metadata.create_all(engine)
モデル定義から自動でテーブルを生成
セッション管理
from sqlmodel import Session
# セッション作成
with Session(engine) as session:
# データベース操作をここに記述
session.commit() # 変更を保存
withブロックで自動的にクローズ
CRUD操作
Create(作成)
user = User(name="田中太郎", email="tanaka@example.com", age=25)
with Session(engine) as session:
session.add(user)
session.commit()
session.refresh(user) # DBから最新データを取得
print(f"作成されたユーザーID: {user.id}")
Read(読み取り)
from sqlmodel import select
with Session(engine) as session:
# 全件取得
statement = select(User)
users = session.exec(statement).all()
# ID指定で1件取得
user = session.get(User, 1)
Update(更新)
with Session(engine) as session:
user = session.get(User, 1)
user.age = 26
session.add(user)
session.commit()
session.refresh(user)
Delete(削除)
with Session(engine) as session:
user = session.get(User, 1)
session.delete(user)
session.commit()
データ検索・絞り込み
条件検索(WHERE)
from sqlmodel import select
with Session(engine) as session:
# 年齢が25歳以上のユーザー
statement = select(User).where(User.age >= 25)
users = session.exec(statement).all()
# 複数条件(AND)
statement = select(User).where(
User.age >= 20,
User.age <= 30
)
users = session.exec(statement).all()
並び替え・件数制限
from sqlmodel import select, col
with Session(engine) as session:
# 年齢の降順で並び替え
statement = select(User).order_by(col(User.age).desc())
# 最新10件のみ取得
statement = select(User).limit(10)
# 組み合わせ
statement = select(User).where(User.age >= 20).order_by(User.name).limit(5)
部分一致検索(LIKE)
from sqlmodel import select, col
with Session(engine) as session:
# 名前に「田中」を含むユーザー
statement = select(User).where(col(User.name).contains("田中"))
# 名前が「山」で始まるユーザー
statement = select(User).where(col(User.name).startswith("山"))
OR条件
from sqlmodel import select, or_
with Session(engine) as session:
# 年齢が20歳未満または30歳以上
statement = select(User).where(
or_(User.age < 20, User.age >= 30)
)
リレーション(1対多)
モデル定義
from sqlmodel import SQLModel, Field, Relationship
from typing import Optional, List
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
# リレーション: 1つのチームは複数のユーザーを持つ
members: List["User"] = Relationship(back_populates="team")
class User(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
team_id: Optional[int] = Field(default=None, foreign_key="team.id")
# リレーション: 1人のユーザーは1つのチームに所属
team: Optional[Team] = Relationship(back_populates="members")
リレーションの作成
with Session(engine) as session:
# チーム作成
team = Team(name="開発チーム")
session.add(team)
session.commit()
session.refresh(team)
# ユーザー作成とチーム紐付け
user = User(name="田中太郎", team_id=team.id)
session.add(user)
session.commit()
リレーションの取得
with Session(engine) as session:
# チームからメンバーを取得
team = session.get(Team, 1)
for member in team.members:
print(member.name)
# ユーザーからチームを取得
user = session.get(User, 1)
print(user.team.name)
Fieldオプション
from sqlmodel import SQLModel, Field
from typing import Optional
class Product(SQLModel, table=True):
# 主キー(自動採番)
id: Optional[int] = Field(default=None, primary_key=True)
# 必須項目
name: str = Field(index=True) # インデックス作成
# デフォルト値
price: int = Field(default=0)
# 最小値・最大値
stock: int = Field(ge=0, le=10000) # 0以上10000以下
# 文字列長制限
code: str = Field(max_length=20)
# ユニーク制約
email: str = Field(unique=True)
# NULL許可
description: Optional[str] = None
実務Tips
トランザクション管理
複数の操作を1つのトランザクションにまとめることで、データの整合性を保つ。 エラー時は自動的にロールバックされる。
N+1問題対策
リレーション取得時はselectinload()やjoinedload()を使って
クエリ数を削減する。
バリデーション
Pydanticの機能を活用して、データ保存前に自動バリデーションを実施。 型安全性を確保できる。
環境変数でDB接続
本番環境では接続文字列を環境変数で管理。
os.getenv("DATABASE_URL")で取得する。
FastAPI連携
基本的なAPI実装
from fastapi import FastAPI, Depends
from sqlmodel import Session, select
app = FastAPI()
# データベース接続の依存関係
def get_session():
with Session(engine) as session:
yield session
# ユーザー一覧取得API
@app.get("/users")
def get_users(session: Session = Depends(get_session)):
statement = select(User)
users = session.exec(statement).all()
return users
# ユーザー作成API
@app.post("/users")
def create_user(user: User, session: Session = Depends(get_session)):
session.add(user)
session.commit()
session.refresh(user)
return user
よくあるエラーと対処法
IntegrityError: UNIQUE constraint failed
原因: ユニーク制約違反(同じメールアドレスなど)
対処: 既存データの確認、または制約の見直し
DetachedInstanceError
原因: セッションが閉じた後にリレーションにアクセス
対処: セッション内でリレーションを先に読み込む、またはselectinload()を使用
テーブルが作成されない
原因: モデル定義時にtable=Trueを忘れている
対処: class User(SQLModel, table=True)と明示的に指定
データベース接続文字列
SQLite(開発用)
sqlite:///database.db
PostgreSQL
postgresql://user:password@localhost/dbname
MySQL
mysql://user:password@localhost/dbname