Python | DB・SQL:migration

Python
スポンサーリンク

migration って何?まずはイメージから

migration(マイグレーション)は、
「データベースの“形”(テーブルやカラムの構造)を、コードでバージョン管理して、少しずつ進化させていく仕組み」です。

最初はテーブルが 1 個だけ
あとからカラムを足したくなる
さらに別テーブルを追加したくなる
既存カラムの名前を変えたくなる

こういう「DB の設計変更」を、手作業の SQL ではなく、
「履歴付きのスクリプト」として管理するのが migration です。

アプリが成長していくほど、
「今のコードに合った DB の形に、確実にアップデートする」ことが重要になります。
そのための道具が migration だと思ってください。


なぜ migration が必要になるのか

「最初の CREATE TABLE だけ」で終わらないから

学習の最初は、だいたいこうです。

CREATE TABLE users (
    id    INTEGER PRIMARY KEY,
    name  TEXT NOT NULL
);
SQL

でも、アプリを作り始めると、すぐにこうなります。

メールアドレスも欲しいな
created_at(作成日時)も欲しい
ユーザーにロール(権限)を持たせたい

そのたびに、DB の構造を変える必要があります。

ALTER TABLE users ADD COLUMN email TEXT;
ALTER TABLE users ADD COLUMN created_at TIMESTAMP;
SQL

これを「本番環境」「ステージング」「開発環境」など、
複数の DB に対して、同じ順番・同じ内容で適用しないといけません。

ここで手作業に頼ると、

どの環境にどこまで適用したか分からなくなる
人によって実行した SQL が微妙に違う
新メンバーが参加したときに DB の状態を再現しづらい

といった問題が一気に噴き出します。

「DB の形」もコードと同じようにバージョン管理したい

アプリのコードは Git でバージョン管理しますよね。
migration は、「DB の形」も同じようにバージョン管理するための仕組みです。

どのバージョンのコードには、どのバージョンの DB 構造が対応しているか
新しいバージョンに上げるときに、どんな変更を DB に適用すべきか
必要なら、前のバージョンに戻す(ロールバック)こともできるか

これを「migration ファイル」という単位で管理します。


Python の世界での migration の代表例

Django の migration(超ざっくり)

Django では、モデルを書き換えると、
makemigrationsmigrate で DB の構造を更新します。

モデルを定義:

from django.db import models

class User(models.Model):
    name = models.CharField(max_length=100)
Python

最初の migration を作る:

python manage.py makemigrations
python manage.py migrate

これで、users テーブルが作られます。

あとから email カラムを追加したくなったら、モデルを変えます。

class User(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(unique=True, null=True)
Python

再度:

python manage.py makemigrations
python manage.py migrate

すると、Django が差分を見て、

「users テーブルに email カラムを追加する」という migration ファイルを作る
それを DB に適用してくれる

という流れになります。

重要なのは、「DB の変更内容が migration ファイルとして残る」ことです。
新しく参加した人は、migrate するだけで、同じ DB 構造を再現できます。

SQLAlchemy + Alembic の migration(ORM + migration の定番)

SQLAlchemy を使う場合、migration ツールとしてよく使われるのが Alembic です。

モデルを定義しておいて、
Alembic に「差分から migration を作らせる」ことができます。

モデル例:

from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
Python

Alembic を初期化して(ここは一度だけ)、
alembic revision --autogenerate -m "create users"
alembic upgrade head

のようにすると、
「users テーブルを作る migration」が生成され、適用されます。

あとから email カラムを追加したら、
再度 --autogenerate で差分を検出して migration を作り、
upgrade で適用する、という流れです。


migration ファイルって何が書いてあるのか

「前の状態 → 新しい状態」への変化をコードで表したもの

migration ファイルは、
「DB をどう変えるか」を表すスクリプトです。

Django の migration ファイルのイメージ:

class Migration(migrations.Migration):

    dependencies = [
        ("app", "0001_initial"),
    ]

    operations = [
        migrations.AddField(
            model_name="user",
            name="email",
            field=models.EmailField(max_length=254, null=True, unique=True),
        ),
    ]
Python

ここには、

前の migration(0001_initial)からの続きで
User モデルに email フィールドを追加する

という情報が書かれています。

Alembic の migration ファイルのイメージ:

from alembic import op
import sqlalchemy as sa

def upgrade():
    op.add_column("users", sa.Column("email", sa.String(), nullable=True))

def downgrade():
    op.drop_column("users", "email")
Python

ここには、

upgrade:新しいバージョンに進めるときにやること(email カラム追加)
downgrade:前のバージョンに戻すときにやること(email カラム削除)

が書かれています。

重要なのは、「変更そのもの」がコードとして残ることです。
これにより、

どんな順番で DB が進化してきたか
どのバージョンでどんな変更が入ったか
必要ならどこまで戻せるか

が、明確になります。


migration の「よくある流れ」を頭に入れておく

1. モデル(またはスキーマ)を変更する

まずは「こういうカラムを足したい」「このテーブルを追加したい」という変更を、
モデル(ORM)やスキーマ定義に反映します。

例:User に email を追加する、Post テーブルを新しく作る、など。

2. migration を生成する(差分から)

Django なら makemigrations
Alembic なら revision --autogenerate

のように、「今の状態」と「前の状態」の差分から migration ファイルを生成します。

ここで大事なのは、「生成された migration をちゃんと目で確認する」ことです。
意図しない変更(カラム削除など)が紛れ込んでいないかをチェックします。

3. migration を適用する

Django なら migrate
Alembic なら upgrade

を実行して、実際の DB に変更を反映します。

このとき、開発環境・テスト環境・本番環境など、
すべての環境で「同じ migration を同じ順番で」適用します。

4. コードと DB の形が揃う

migration を適用し終わると、
「今のコードが期待している DB の形」と、
「実際の DB の形」が一致します。

これが崩れると、

コードは email カラムを前提に動いているのに、DB に email がない
逆に、DB にはあるけどコードが知らない

といった不整合が起きて、バグやエラーの原因になります。


migration で特に意識してほしい重要ポイント

「手作業で直接 ALTER しない」習慣をつける

本番 DB に対して、
いきなり手で ALTER TABLE を打つのは、基本的にやめた方がいいです。

理由はシンプルで、

その変更が履歴に残らない
他の環境に同じ変更を再現しづらい
新メンバーが入ったときに「何が起きたか」が分からない

からです。

「DB の構造を変えるときは、必ず migration を通す」
というルールを自分の中に作っておくと、
後々の自分がかなり楽になります。

「破壊的な変更」は特に慎重に

カラム削除、テーブル削除、型変更などの「破壊的な変更」は、
migration でも特に慎重に扱う必要があります。

データが消える可能性がある
既存のコードが前提としているカラムがなくなる
ロールバックが難しくなる

などのリスクがあるからです。

実務では、

まずは「使わないカラム」を論理的に無効化(アプリから使わない)
しばらく様子を見て、本当に不要になったら削除 migration を書く

といった段階的なアプローチを取ることが多いです。


まとめ(migration は「DB の進化の履歴書」)

migration を初心者目線でまとめると、こうなります。

アプリの成長に合わせて変わっていく「DB の形(テーブル・カラム・制約)」を、コードとしてバージョン管理する仕組みが migration。
Django なら makemigrations / migrate、SQLAlchemy なら Alembic などを使って、「モデルの変更 → migration 生成 → 適用」という流れで DB を進化させる。
migration ファイルには、「前の状態から新しい状態にどう変えるか」が書かれていて、履歴として残るので、環境間で同じ DB 構造を再現しやすい。
DB を直接いじるのではなく、「DB の構造変更は必ず migration 経由で」という習慣をつけると、後からの保守・チーム開発・本番運用が圧倒的に楽になる。

タイトルとURLをコピーしました