はじめに
FastAPI や SQLAlchemy を使って開発していると、どうしても避けられないのが:
- テーブルの追加
- カラムの追加
- カラム名変更
- 型変更
- インデックス追加
- 制約の追加・削除
これが “野生の ALTER TABLE” で行われると、プロダクトは 確実に崩壊する。
そこで登場するのが Alembic。
Alembic は:
- DBスキーマを「コードとして」バージョン管理する
- 変更を upgrade/downgrade で自由に行き来できる
- ステージング・本番・ローカルを常に同じ状態に保つ
という、実務で必須のツール。
1. Alembic の特徴とメリット(実務目線)
1.1 スキーマ変更を「履歴化」できる
すべての変更は revision として蓄積される。
例:
alembic/
versions/
20250101_create_user_table.py
20250103_add_email_column.py
これにより:
- スキーマ変更の“責任者”と“目的”が明確
- 過去に戻せる
- 規模が大きくなっても変更管理が破綻しない
1.2 SQLAlchemy モデルと同期できる(autogenerate)
変更後のモデルと DB の実際を比較し、差分だけを自動生成する:
alembic revision --autogenerate -m "add email"
自動でマイグレーションが生成される。
実務では 8割自動 / 2割手動 が最も安全。
1.3. どの環境でも統一できる
alembic upgrade head
全員が 同じスキーマバージョンになる。
チーム開発での事故率が激減。
1.4. 本番移行が安全
CI/CD で自動化すれば、“本番に勝手に ALTER が走る事故”を防止。
2. Alembic 導入(FastAPI × SQLAlchemy 例)
ディレクトリ構成
project/
alembic/
versions/
env.py
script.py.mako
alembic.ini
app/
db/
base.py
session.py
models/
user.py
item.py
初期化コマンド
alembic init alembic
alembic/ と alembic.ini が生成される。
env.py – metadata を読み込む(超重要)
# alembic/env.py
from app.db.base import Base # Base.metadata を使う
target_metadata = Base.metadata
これがないと autogenerate が完全に無反応になる。
3. 初リビジョン作成とマイグレーション
3.1 モデルを書く
# app/models/user.py
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
email: Mapped[str | None]
3.2 Autogenerate 実行
alembic revision --autogenerate -m "create users table"
Alembic が差分を検知し、versions/ にファイル生成。
3.3 マイグレーション適用
alembic upgrade head
DB にテーブルが作成される。
4. よくある変更パターンとマイグレーション例
カラム追加
age: Mapped[int | None]
autogenerate:
op.add_column('users', sa.Column('age', sa.Integer(), nullable=True))
カラム名変更(autogenerateでは気付かない)
SQLAlchemy のモデル変更だけでは検出されず drop/add になる可能性が高いので、
op.alter_column("users", "old_name", new_column_name="new_name")
を手動で書く必要あり。
インデックス追加
from sqlalchemy import Index
Index("ix_users_email", User.email)
autogenerate は比較的優秀で検出される。
制約(CHECK, UNIQUE, FK)
変更検出がイマイチなので必ずレビュー。
5. Autogenerate が信頼できる部分 / 危険な部分
信頼できる
- テーブル追加
- カラム追加
- 型変更(Integer → String 以外)
- FK追加/削除
- Index 追加
危険な部分(レビュー必須)
| ケース | 理由 |
|---|---|
| Enum の内容変更 | DB の Enum は差分判定が難しい |
| server_default の変更 | “変更”が検知されない or 誤検知 |
| Boolean default の変更 | DB により挙動が違う |
| constraint の削除 | 誤検知 or 未検知 |
| rename column | drop/add と誤認する |
結論:
autogenerate は 80% 完璧、20% は人間の眼で守る。
6. SQLAlchemy モデルを dataclass にしてはいけない理由
最近「Pydantic v2 = dataclasses ベース」なので誤解が多い。
❌ SQLAlchemy モデルに dataclass を付けると:
-
__init__が上書きされる - default の扱いが壊れる
- metadata の反映が崩れて autogenerate が混乱
結論:SQLAlchemy モデルは dataclass にしない
(本当に必要なときだけ高度設定)
7. 実務ワークフロー
① モデルを変更する
limit: Mapped[int] = mapped_column(default=10)
② autogenerate 実行
alembic revision --autogenerate -m "add limit to users"
③ 差分をレビュー(必須)
- DROP が混ざっていないか
- default が正しいか
- rename が drop/add になっていないか
- foreign key が誤検知してないか
④ upgrade で適用
alembic upgrade head
⑤ 本番も upgrade 経由で反映
CI/CD に統合するのがベスト。
8. 実務で起きがちなトラブルと解決方法
❌ autogenerate が何も検出しない
→ ほぼ 100% metadata が読み込まれていない
→ env.py を確認すべき
❌ autogenerate がやたら DROP しまくる
→ モデルの import 順序・重複定義・循環参照が原因
❌ 本番で手動 ALTER したら破壊された
→ Alembic の履歴と DB がズレた
→ 原則:DB に直接触らないこと
❌ downgrade で戻らない
→ downgrade の実装はあくまで“自作”だから当然
→ 本番運用では downgrade を使わないのが一般的
9. プロダクション向け Tips
DB変更は複数ステップで行う
例:大規模 rename や型変換は“安全移行パターン”を使う
- 新カラム追加
- データ移行
- 新カラムに切り替え
- 古カラム削除
Alembic 操作は CI/CD に統合する
例:
GitHub Actions で:
alembic upgrade head
本番で絶対にやってはいけない
- 大量テーブルの rename
- 大量データの delete
- autogenerate の無レビュー反映
まとめ
Alembic はただのツールではなく:
- スキーマの歴史を管理し
- 誰が見ても理解できる状態を保ち
- プロダクトの寿命を守る
使えば使うほど 本当の価値がわかるツール。