SqlAlchemy-migrateを使ってみる

SqlAlchemy-migrateを使ってみる

概要

RailsActiveRecordのMigrationに影響を受けて、SQLAlchemy を使ったプロジェクトのスキーマ管理をできるようにしたもの。

現時点での最新Verは0.8.2みたいです。

Package Index > sqlalchemy-migrate > 0.8.2

使用方法

ライブラリのインストール

$ pip install sqlalchemy
$ pip install sqlalchemy-migrate

これにより"migrate"コマンドが使えるようになる。

$ migrate --help

プロジェクトの作成

※DBは既に存在していることを前提とする。今回はチュートリアルにのっとりSQLiteを使用します。

ちなみに初期Tableはこんな感じで作ってある。

# create_table.py

from sqlalchemy import *

engine = create_engine('sqlite:///test.db', echo=True)

meta = MetaData()

city = Table('city', meta,
             Column('city_id', INTEGER, primary_key=True),
             Column('city_name', String(32)))

meta.create_all(engine)

まずはプロジェクトを作る。

$ migrate create test_migrate "Test Project"
$ tree
.
└── test_migrate
├── README
├── __init__.py
├── __init__.pyc
├── manage.py
├── migrate.cfg
└── versions
    ├── __init__.py
    └── __init__.pyc

データベースの初期化

$ cd test_migrate/
$ python manage.py version_control sqlite:///test.db .

毎回、データベース情報引数で渡すのはめんどいので登録しておく。

$ migrate manage manage.py --repository=. --url=sqlite:///test.db

DB見るとマイグレーション用のTableができてる。

$ sqlite3 test.db
$ .explain ON
$ .tables
city             migrate_version


sqlite> select * from city;
city  city_name
----  -------------
1     Los Angels
2     San Francisco

sqlite> select * from migrate_version;
repo  repository_pa  vers
----  -------------  ----
Test Project  .       0

現在のスキーマバージョン。

$ python manage.py db_version
  0

登録されているスキーマバージョンの確認。

$ python manage.py version
  0

スキーマ変更用のスクリプトの作成

まずは、スキーマ変更用のスクリプトを登録する。

$ python manage.py script "Add country table"
$ cd versions/
  001_Add_country_table.py  __init__.py  __init__.pyc

001_Add_country_table.pyを書き換える。

# 001_Add_country_table.py

from sqlalchemy import *
from migrate import *

meta = MetaData()

country = Table('country', meta,
                Column('id', Integer, primary_key=True),
                Column('country_name', String(32))
                )


def upgrade(migrate_engine):
    meta.bind = migrate_engine
    country.create()


def downgrade(migrate_engine):
    meta.bind = migrate_engine
    country.drop()

ちゃんと動くかテストする。

$ python manage.py test
  Upgrading...
  done
  Downgrading...
  done
  Success

現在のスキーマバージョン、登録されているスキーマバージョンを確認してみる。 versionが1上がってますね。

$ python manage.py db_version
  0
$ python manage.py version
  1

スキーマを上げる

$ python manage.py upgrade
  0 -> 1...
  done

バージョンが上がっているか確認。

$ python manage.py db_version
  1

sqliteの中身みてみる。countryが追加されてる。

 sqlite> .tables
    city             country          migrate_version
    sqlite> .schema country
    CREATE TABLE country (
        id INTEGER NOT NULL,
        country_name VARCHAR(32),
        PRIMARY KEY (id)
    );

ダウングレードしてみる

できてますね。(countoryが消えてる。)

$ python manage.py downgrade 0
  1 -> 0...
  done

sqlite> .tables
city             migrate_version

元に戻してみてもOK。

$ python manage.py upgrade
  0 -> 1...
  done

sqlite> .tables
city             country          migrate_version

カラムの追加

countryテーブルにpopulationカラムを追加してみる。 スクリプトが追加されてますね。

$ python manage.py script "Add population column in country table"
$ ls versions/
001_Add_country_table.py            __init__.py
001_Add_country_table.pyc           __init__.pyc
002_Add_population_column_in_country_table.py

002_Add_population_column_in_country_table.pyを書き換えてみる。

# 002_Add_population_column_in_country_table.py

from sqlalchemy import *
from migrate import *


def upgrade(migrate_engine):
    meta = MetaData(bind=migrate_engine)
    country = Table('country', meta, autoload=True)
    population_col = Column('population', Integer)
    population_col.create(country)


def downgrade(migrate_engine):
    meta = MetaData(bind=migrate_engine)
    country = Table('country', meta, autoload=True)
    country.c.population.drop()

テストしてみる。いけそう。

$ python manage.py test
  Upgrading...
  done
  Downgrading...
  done
  Success

現在のスキーマバージョン、登録されているスキーマバージョンを確認してみる。

$ python manage.py db_version
  1
$ python manage.py version
  2

アップデートしてみる。populationカラムが追加されてますね。

$ python manage.py upgrade
1 -> 2...
done

sqlite> .schema country
CREATE TABLE country (
    id INTEGER NOT NULL,
    country_name VARCHAR(32), population INTEGER,
    PRIMARY KEY (id)
);

現在のスキーマバージョン、登録されているスキーマバージョンを確認してみる。

$ python manage.py db_version
  2
$ python manage.py version
  2

複数バージョン間の移動

もちろんver2からver0、ver0からver2とかもできる。

$ python manage.py downgrade 0
2 -> 1...
done
1 -> 0...
done

$ python manage.py upgrade 2
0 -> 1...
done
1 -> 2...
done

まとめ

SqlAlchemy-migrate非常に便利ですね。Flaskとかとも組み合わせて使ってみたい。