- Python
- データベース
Alembicの使い方|SQLAlchemyのマイグレーション手順
Alembic は、SQLAlchemy 用のマイグレーション(データベースの構造の変更を、履歴つきのスクリプトで管理する)ツールです。 モデル(Python のクラス)を変えたら、alembic revision --autogenerate で差分のスクリプトを作り、alembic upgrade head でデータベースに当てます。
pip install alembic
alembic init migrations # 1. 初期化
# migrations/env.py に target_metadata を設定 # 2. モデルとつなぐ
alembic revision --autogenerate -m "create users" # 3. 差分からスクリプトを作る
alembic upgrade head # 4. 最新まで当てる
alembic downgrade -1 # 5. 1つ戻す
autogenerate は、すべての変更を見つけるわけではありません。 手元で試した結果です。
| モデルの変更 | autogenerate の結果 | 対応 |
|---|---|---|
| テーブル・列の追加・削除 | 検出 | そのまま使える |
型の変更(String(200) → String(300)) |
検出(compare_type は既定で有効) |
そのまま使える |
| インデックスの追加 | 検出 | そのまま使える |
| 列名の変更 | 「列の削除+列の追加」になる(データが消える) | op.alter_column(new_column_name=...) を手で書く |
| テーブル名の変更 | 「テーブルの削除+作成」になる | op.rename_table を手で書く |
| server_default の変更 | 検出しない(compare_server_default=True で検出) |
設定を足す |
| 名前付きの CHECK 制約 | 検出しない(プラグインで検出) | autogenerate_plugins に足す |
| PostgreSQL の Enum に値を追加 | 検出しない | ALTER TYPE ... ADD VALUE を手で書く |
| データが入ったテーブルに NOT NULL 列 | スクリプトは作られるが、upgrade で失敗 | 3段階に分けて書く |
確認環境:Alembic 1.20.0・SQLAlchemy 2.1.4・psycopg 3.3.6(pip で導入)、Python 3.11、PostgreSQL 16.15(Ubuntu 24.04 の apt パッケージ)。SQLite(3.45.1)でも batch モードを試しました。この記事のスクリプトとログは、実際にコマンドを実行して出たものです。検出の範囲は Alembic の公式ドキュメント「Auto Generating Migrations」でも確かめました(確認日 2026年10月13日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Alembicの使い方|SQLAlchemyのマイグレーション手順」
1. インストールと alembic init
pip install alembic "psycopg[binary]" # PostgreSQL のドライバも入れる
alembic list_templates
Available templates:
pyproject_async - pyproject configuration, with an async dbapi.
multidb - Rudimentary multi-database configuration.
generic - Generic single-database configuration.
pyproject - pyproject configuration, based on the generic configuration.
async - Generic single-database configuration with an async dbapi.
既定の generic テンプレートで初期化します。
alembic init migrations
Creating directory …/migrations ... done
Creating directory …/migrations/versions ... done
Generating …/migrations/script.py.mako ... done
Generating …/migrations/README ... done
Generating …/migrations/env.py ... done
Generating …/alembic.ini ... done
Please edit configuration/connection/logging settings in …/alembic.ini before proceeding.
| ファイル | 役割 |
|---|---|
alembic.ini |
設定(script_location・sqlalchemy.url・ログ・post_write_hooks) |
migrations/env.py |
接続とモデルのつなぎ込み。ここを編集する |
migrations/script.py.mako |
新しいスクリプトのひな形 |
migrations/versions/ |
マイグレーションのスクリプトが入る |
設定を pyproject.toml の [tool.alembic] に書きたいときは alembic init -t pyproject migrations、非同期のドライバ(asyncpg など)を使うときは -t async を選びます。
2. モデルと env.py をつなぐ
SQLAlchemy 2.0 の書き方(DeclarativeBase・Mapped)のモデルです。MetaData に naming_convention(制約の名前の付け方)を渡しておくと、制約に必ず名前が付き、あとで削除・変更するスクリプトが安定します。
# app/models.py
from datetime import datetime
from sqlalchemy import ForeignKey, MetaData, String, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
NAMING_CONVENTION = {
"ix": "ix_%(column_0_label)s",
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(constraint_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s",
}
class Base(DeclarativeBase):
metadata = MetaData(naming_convention=NAMING_CONVENTION)
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
email: Mapped[str] = mapped_column(String(255), unique=True)
name: Mapped[str] = mapped_column(String(100))
created_at: Mapped[datetime] = mapped_column(server_default=func.now())
posts: Mapped[list["Post"]] = relationship(back_populates="user")
class Post(Base):
__tablename__ = "posts"
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
title: Mapped[str] = mapped_column(String(200))
user: Mapped[User] = relationship(back_populates="posts")
migrations/env.py は、次の2か所を変えます。
# migrations/env.py(変えた部分)
import os
from alembic import context
from app.models import Base
config = context.config
# 接続先は環境変数から(alembic.ini にパスワードを書かない)
if os.environ.get("DATABASE_URL"):
config.set_main_option("sqlalchemy.url", os.environ["DATABASE_URL"])
# autogenerate が比べる相手(モデルの MetaData)
target_metadata = Base.metadata
export DATABASE_URL="postgresql+psycopg://app:****@localhost/appdb"
alembic.ini には既定で prepend_sys_path = . があり、alembic.ini のあるディレクトリから app.models を import できます。target_metadata = None のままだと、autogenerate はモデルを見ないので、差分を作れません。 モデルが複数のファイルに分かれているときは、すべてのモデルが import された状態の Base.metadata を渡します。
3. revision --autogenerate で最初のスクリプトを作る
alembic revision --autogenerate -m "create users and posts"
INFO [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO [alembic.runtime.migration] Will assume transactional DDL.
INFO [alembic.autogenerate.compare.tables] Detected added table 'users'
INFO [alembic.autogenerate.compare.tables] Detected added table 'posts'
Generating …/migrations/versions/0da4d0d75970_create_users_and_posts.py ... done
できたスクリプトです。
"""create users and posts
Revision ID: 0da4d0d75970
Revises:
Create Date: 2026-10-13 16:14:52.988509
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '0da4d0d75970'
down_revision: Union[str, Sequence[str], None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('email', sa.String(length=255), nullable=False),
sa.Column('name', sa.String(length=100), nullable=False),
sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False),
sa.PrimaryKeyConstraint('id', name=op.f('pk_users')),
sa.UniqueConstraint('email', name=op.f('uq_users_email'))
)
op.create_table('posts',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('title', sa.String(length=200), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], name=op.f('fk_posts_user_id_users')),
sa.PrimaryKeyConstraint('id', name=op.f('pk_posts'))
)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('posts')
op.drop_table('users')
# ### end Alembic commands ###
| 項目 | 意味 |
|---|---|
revision |
このスクリプトの ID |
down_revision |
1つ前の ID(最初は None)。この鎖で順番が決まる |
upgrade() |
進めるときの処理 |
downgrade() |
戻すときの処理 |
op.f('pk_users') |
naming_convention で決めた名前を、そのまま使う印 |
Mapped[str] は NOT NULL、Mapped[str | None] は NULL 可として出力されました。制約の名前が pk_users・uq_users_email・fk_posts_user_id_users と、naming_convention どおりになっています。
4. upgrade・current・history
alembic upgrade head
INFO [alembic.runtime.migration] Running upgrade -> 0da4d0d75970, create users and posts
alembic current
# 0da4d0d75970 (head)
データベースには、テーブルのほかに alembic_version というテーブルができ、いまの版の ID が1行入りました。
version_num
--------------
0da4d0d75970
psql の \d posts では、制約とインデックスが naming_convention の名前で作られていました。
Indexes:
"pk_posts" PRIMARY KEY, btree (id)
Foreign-key constraints:
"fk_posts_user_id_users" FOREIGN KEY (user_id) REFERENCES users(id)
5. モデルを変えて2つ目のスクリプトを作る
モデルを3か所変えました。
bio: Mapped[str | None] = mapped_column(String(500)) # 列の追加
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), index=True) # インデックス
title: Mapped[str] = mapped_column(String(300)) # 200 → 300
スクリプトを作る前に、alembic check でモデルとデータベースの差を確かめられます。
$ alembic check
INFO [alembic.autogenerate.compare.types] Detected type change from VARCHAR(length=200) to String(length=300) on 'posts.title'
INFO [alembic.autogenerate.compare.constraints] Detected added index 'ix_posts_user_id' on '('user_id',)'
INFO [alembic.autogenerate.compare.tables] Detected added column 'users.bio'
FAILED: New upgrade operations detected: [...]
差があると終了コードが 255、無いと No new upgrade operations detected. で 0 でした。CI で「モデルを変えたのにスクリプトを作り忘れた」を見つけるのに使えます。
alembic revision --autogenerate -m "add bio and index"
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('posts', 'title',
existing_type=sa.VARCHAR(length=200),
type_=sa.String(length=300),
existing_nullable=False)
op.create_index(op.f('ix_posts_user_id'), 'posts', ['user_id'], unique=False)
op.add_column('users', sa.Column('bio', sa.String(length=500), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('users', 'bio')
op.drop_index(op.f('ix_posts_user_id'), table_name='posts')
op.alter_column('posts', 'title',
existing_type=sa.String(length=300),
type_=sa.VARCHAR(length=200),
existing_nullable=False)
# ### end Alembic commands ###
型の変更(compare_type)は、Alembic 1.12.0 から既定で検出されます。 ドキュメントにも「Changed in version 1.12.0: The default value of compare_type has been changed to True」とあります。
6. autogenerate で検出されない変更と直し方
列名の変更:削除+追加になる
name を display_name に変えて alembic check を実行すると、次のように出ました。
INFO [alembic.autogenerate.compare.tables] Detected added column 'users.display_name'
INFO [alembic.autogenerate.compare.tables] Detected removed column 'users.name'
このまま upgrade すると、name 列のデータが消えます。 Alembic には「名前が変わった」ことは分からないためです。中身が空のスクリプトを作って、手で書きます。
alembic revision -m "rename users.name to display_name"
def upgrade() -> None:
op.alter_column("users", "name", new_column_name="display_name")
def downgrade() -> None:
op.alter_column("users", "display_name", new_column_name="name")
2行入れた状態で upgrade すると、データは残ったまま列名だけが変わり、そのあとの alembic check は No new upgrade operations detected. でした。
id | email | display_name
----+---------------+--------------
1 | a@example.com | Aoki
2 | b@example.com | Baba
テーブル名の変更も同じです。 __tablename__ を posts から articles に変えると、Detected added table 'articles'・Detected removed table 'posts' になりました。op.rename_table("posts", "articles") を手で書きます。
server_default の変更:既定では検出しない
server_default=func.now() を func.statement_timestamp() に変えても、alembic check は No new upgrade operations detected. でした。env.py の context.configure に compare_server_default=True を足すと、detected server default on column 'users.created_at' と検出されました。
context.configure(
connection=connection,
target_metadata=target_metadata,
compare_server_default=True,
)
ドキュメントでも、server default の比較は「既定では無効(compare_server_default で有効)」とされています。有効にしたら、生成されたスクリプトを必ず読み、意図しない差分が入っていないかを確かめます。
名前付きの CHECK 制約:プラグインを足す
CheckConstraint("length(title) > 0", name="title_not_empty") を足しても、既定では検出されませんでした。Alembic 1.19.0 で加わった alembic.ext.checkconstraint_byname プラグインを有効にすると、検出されました(1.19.2 から既定では無効)。
context.configure(
connection=connection,
target_metadata=target_metadata,
autogenerate_plugins=[
"alembic.autogenerate.*",
"alembic.ext.checkconstraint_byname",
],
)
def upgrade() -> None:
op.create_check_constraint(op.f('ck_posts_title_not_empty'), 'posts', 'length(title) > 0')
def downgrade() -> None:
op.drop_constraint(op.f('ck_posts_title_not_empty'), 'posts', type_='check')
名前で比べる仕組みなので、名前の無い制約は検出できません。 naming_convention を設定しておく理由の1つです。
PostgreSQL の Enum:型の作成と値の追加
Enum("public", "private", name="visibility") の列を既存のテーブルに足すと、autogenerate は op.add_column(... sa.Enum(...) ...) を作りました。これをそのまま upgrade すると、PostgreSQL で失敗しました。
sqlalchemy.exc.ProgrammingError: (psycopg.errors.UndefinedObject) type "visibility" does not exist
[SQL: ALTER TABLE posts ADD COLUMN visibility visibility DEFAULT 'public' NOT NULL]
create_table と違い、add_column では Enum の型が作られなかったためです。型を先に作り、downgrade で型も消すように直すと、upgrade・downgrade とも通りました。
def upgrade() -> None:
visibility = sa.Enum("public", "private", name="visibility")
visibility.create(op.get_bind(), checkfirst=True) # 型を先に作る
op.add_column("posts", sa.Column("visibility", visibility, server_default="public", nullable=False))
def downgrade() -> None:
op.drop_column("posts", "visibility")
sa.Enum(name="visibility").drop(op.get_bind(), checkfirst=True) # 型も消す
さらに、モデルの Enum に "draft" を足しても、alembic check は No new upgrade operations detected. でした。値の追加は手で書きます。
def upgrade() -> None:
op.execute("ALTER TYPE visibility ADD VALUE IF NOT EXISTS 'draft'")
upgrade 後、enum_range(null::visibility) は {public,private,draft} になりました。PostgreSQL の Enum から値を消す SQL は無いので、downgrade は型の作り直しが必要になります。
データが入ったテーブルに NOT NULL の列を足す
status: Mapped[str] = mapped_column(String(20))(NOT NULL)を足すと、autogenerate は op.add_column('users', sa.Column('status', sa.String(length=20), nullable=False)) を作りました。2行入ったテーブルに upgrade すると、失敗しました。
psycopg.errors.NotNullViolation: column "status" of relation "users" contains null values
PostgreSQL は DDL もトランザクションに入るため、失敗したスクリプトは丸ごと取り消され、alembic current は1つ前の版のままでした(ログの Will assume transactional DDL. がその印です)。3段階に書き直して通しました。
def upgrade() -> None:
# 1. NULL を許して追加 → 2. 既存の行を埋める → 3. NOT NULL にする
op.add_column("users", sa.Column("status", sa.String(length=20), nullable=True))
op.execute("UPDATE users SET status = 'active' WHERE status IS NULL")
op.alter_column("users", "status", nullable=False)
既定値で埋めてよい列なら、server_default="active" を付けて1回で追加する方法もあります。NOT NULL のエラーは、関連記事「not-null constraintの直し方|PostgreSQL」で扱っています。
7. downgrade と履歴
alembic history
8f854eccb59f -> 7f79a5e74a3e (head), add users.status
f35e707d34d6 -> 8f854eccb59f, rename users.name to display_name
0b973d64061f -> f35e707d34d6, add title check
0da4d0d75970 -> 0b973d64061f, add bio and index
<base> -> 0da4d0d75970, create users and posts
$ alembic downgrade -1
INFO [alembic.runtime.migration] Running downgrade 7f79a5e74a3e -> 8f854eccb59f, add users.status
$ alembic downgrade 0b973d64061f
INFO [alembic.runtime.migration] Running downgrade 8f854eccb59f -> f35e707d34d6, rename users.name to display_name
INFO [alembic.runtime.migration] Running downgrade f35e707d34d6 -> 0b973d64061f, add title check
$ alembic downgrade base
INFO [alembic.runtime.migration] Running downgrade 0b973d64061f -> 0da4d0d75970, add bio and index
INFO [alembic.runtime.migration] Running downgrade 0da4d0d75970 -> , create users and posts
downgrade base のあと、データベースに残ったのは alembic_version テーブルだけでした。
コマンド早見表
| コマンド | 内容 |
|---|---|
alembic upgrade head |
最新まで進める |
alembic upgrade +1 / alembic downgrade -1 |
1つ進める/戻す |
alembic upgrade <ID> / alembic downgrade <ID> |
指定の版まで |
alembic downgrade base |
すべて戻す |
alembic current |
いまの版 |
alembic history |
履歴(--verbose で詳しく) |
alembic heads |
最新の版(複数あれば分岐している) |
alembic check |
モデルとの差があれば失敗(CI 向け) |
alembic stamp head |
スクリプトを動かさずに、版だけ記録する |
alembic upgrade A:B --sql |
SQL を出力するだけ(オフライン) |
alembic merge heads -m "..." |
分かれた head をまとめる |
stamp は、Base.metadata.create_all() で作った既存のデータベースに Alembic を入れるときに使います。alembic stamp head を実行すると Running stamp_revision -> ebf3a21358f7 と表示され、テーブルには触れずに alembic_version だけが書き換わりました。
SQL だけを出す(DBA にレビューしてもらうとき)
alembic upgrade 0da4d0d75970:0b973d64061f --sql
BEGIN;
-- Running upgrade 0da4d0d75970 -> 0b973d64061f
ALTER TABLE posts ALTER COLUMN title TYPE VARCHAR(300);
CREATE INDEX ix_posts_user_id ON posts (user_id);
ALTER TABLE users ADD COLUMN bio VARCHAR(500);
UPDATE alembic_version SET version_num='0b973d64061f' WHERE alembic_version.version_num = '0da4d0d75970';
COMMIT;
本番に流す SQL を事前に確かめたり、DB に直接つなげない環境で手で流したりするときに使えます。ALTER TABLE の書き方は、関連記事「ALTER TABLEでカラム追加・型変更|MySQL・PostgreSQL比較」で扱っています。
8. 複数の head(ブランチで同時にスクリプトを作った)
2人が同じ版から別々にスクリプトを作ると、head が2つになります。再現すると次のようになりました。
$ alembic heads
aaaa00000001 (head)
bbbb00000002 (head)
$ alembic upgrade head
FAILED: Multiple head revisions are present for given argument 'head'; please specify a specific target revision, '<branchname>@head' to narrow to a specific head, or 'heads' for all heads
alembic merge heads で、2つを親に持つスクリプトを作ってまとめます。
alembic merge heads -m "merge feature a and b"
# 生成されたスクリプト(抜粋)
"""merge feature a and b
Revises: aaaa00000001, bbbb00000002
"""
down_revision: Union[str, Sequence[str], None] = ('aaaa00000001', 'bbbb00000002')
$ alembic upgrade head
INFO [alembic.runtime.migration] Running upgrade 7f79a5e74a3e -> bbbb00000002, feature b
INFO [alembic.runtime.migration] Running upgrade 7f79a5e74a3e -> aaaa00000001, feature a
INFO [alembic.runtime.migration] Running upgrade aaaa00000001, bbbb00000002 -> ebf3a21358f7, merge feature a and b
$ alembic heads
ebf3a21358f7 (head)
2つのスクリプトが同じテーブルを変えていないかは、merge の前に読んで確かめます。CI で alembic heads の行数が1行かを確かめておくと、マージ前に気づけます。
9. SQLite では batch モード(render_as_batch)
SQLite の ALTER TABLE は、制約の追加などに対応していません。SQLite のデータベースで、code 列に UNIQUE を付けて memo 列を消す変更を autogenerate すると、upgrade で次のエラーになりました。
NotImplementedError: No support for ALTER of constraints in SQLite dialect. Please refer to the batch mode feature which allows for SQLite migrations using a copy-and-move strategy.
env.py の context.configure に render_as_batch=True を足すと、autogenerate が batch_alter_table の形で出力します。
context.configure(
connection=connection,
target_metadata=target_metadata,
render_as_batch=True, # SQLite 向け:テーブルを作り直す方式で ALTER する
)
def upgrade() -> None:
with op.batch_alter_table('items', schema=None) as batch_op:
batch_op.create_unique_constraint(batch_op.f('uq_items_code'), ['code'])
batch_op.drop_column('memo')
この形では upgrade が通り、テーブルは次の定義に作り直されました。downgrade も通りました。
CREATE TABLE "items" (
id INTEGER NOT NULL,
code VARCHAR(20) NOT NULL,
CONSTRAINT pk_items PRIMARY KEY (id),
CONSTRAINT uq_items_code UNIQUE (code)
)
SQLite の使い方は、関連記事「SQLiteの使い方|コマンド早見表とPython連携を実行例で解説」で扱っています。
10. 運用のチェックリスト
| 確かめること | 方法 |
|---|---|
| 生成されたスクリプトを読んだか | 列名・テーブル名の変更が「削除+追加」になっていないか |
| downgrade も動くか | 開発環境で upgrade head → downgrade -1 → upgrade head |
| モデルとスクリプトがそろっているか | CI で alembic check |
| head が1つか | CI で alembic heads |
| 本番の SQL を確かめたか | alembic upgrade A:B --sql |
| 大きなテーブルのロック | 列の追加・インデックス作成がテーブルをロックしないか(PostgreSQL なら CREATE INDEX CONCURRENTLY を検討) |
| 接続情報を ini に書いていないか | env.py で環境変数から読む |
生成されたスクリプトを Ruff などで整えたいときは、alembic.ini の [post_write_hooks] を使えます。テンプレートには ruff.options = check --fix REVISION_SCRIPT_FILENAME の例がコメントで入っていました。Ruff の使い方は、関連記事「Ruffの使い方|pyproject.toml設定とルール一覧」で扱っています。
よくある質問
Q. Base.metadata.create_all() と Alembic はどう使い分けますか。
A. create_all() は「無いテーブルを作る」だけで、列の追加や変更はできません。本番のように、データを残したまま構造を変えていくデータベースは Alembic で管理し、テスト用の使い捨てのデータベースだけ create_all() を使う形がよく使われます。モデルの書き方は、関連記事「SQLAlchemy 2.0の使い方|CRUD・リレーションを動くコードで解説」で扱っています。
Q. autogenerate で Detected sequence named 'users_id_seq' ... assuming SERIAL and omitting と出ます。
A. PostgreSQL の SERIAL の列が持つシーケンスを、Alembic が「SERIAL の一部」と判断して比較から外したという INFO のメッセージです。差分ではありません。
Q. 制約の名前はどう決めればよいですか。
A. この記事の naming_convention は、pk_テーブル名・uq_テーブル名_列名・fk_テーブル名_列名_参照先 の形です。命名の考え方は、関連記事「DB命名規則一覧|テーブル名・カラム名・インデックス名・制約名」で扱っています。
Q. FastAPI のアプリで使えますか。
A. 同じ手順で使えます。モデルの Base.metadata を env.py の target_metadata に渡し、起動前に alembic upgrade head を実行します。FastAPI の作り方は、関連記事「FastAPI入門|uvで作るCRUD API完成コードと実行結果」で扱っています。
まとめ
- init → env.py に
target_metadata→revision --autogenerate→upgrade headが基本の流れ - naming_convention を最初に決める。制約に名前が付き、downgrade や CHECK の検出が安定する
- 列名・テーブル名の変更は「削除+追加」になるので、
alter_column(new_column_name=...)・rename_tableを手で書く - server_default は
compare_server_default=True、CHECK はプラグイン、Enum の値は手で書く - データ入りのテーブルに NOT NULL 列は 追加 → UPDATE → NOT NULL の3段階
- CI で
alembic check(差があれば 255)とalembic headsを確かめる
参考・出典
確認日はいずれも 2026年10月13日です。
- Alembic ドキュメント「Tutorial」:https://alembic.sqlalchemy.org/en/latest/tutorial.html
- Alembic ドキュメント「Auto Generating Migrations」:https://alembic.sqlalchemy.org/en/latest/autogenerate.html
- Alembic ドキュメント「The Importance of Naming Constraints」:https://alembic.sqlalchemy.org/en/latest/naming.html
- Alembic ドキュメント「Running "Batch" Migrations for SQLite and Other Databases」:https://alembic.sqlalchemy.org/en/latest/batch.html
- Alembic ドキュメント「Working with Branches」:https://alembic.sqlalchemy.org/en/latest/branches.html
- Alembic ドキュメント「Operation Reference」:https://alembic.sqlalchemy.org/en/latest/ops.html
- alembic(PyPI):https://pypi.org/project/alembic/
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Alembicの使い方|SQLAlchemyのマイグレーション手順」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。