シンプルなPythonのDBマイグレーションツールsimple-db-migrateとは
Webサービスを継続的に開発運用していくうえで
データベースのデータ構造の更新は避けられませんね。
複数人で開発する時に必須と言っても良いソースコードのバージョン管理ですが
ソースコードのみならず、データベースのバージョン管理をすることで
すでに存在するデータを壊さずに必要になった構造変更などを正確かつ便利に実行していくため
データベースのマイグレーションツールを利用することが多くなってきました。
Ruby on RailsやCodeIgniter、Djangoなどフレームワークとセットで
マイグレーションの機能を利用できるものもありますが
今回はそれ単体で動くPythonのDBマイグレーションツールであるsimple-db-migrateを使ってみました。
理由としては
というところです。
元々flywayを使っていたせいか、Webフレームワークのモデルに密結合しているような
マイグレーションを行いたくない、という好みもありました。
simple-db-migrateを利用する
GitHubのリポジトリのREADMEと公式のドキュメントがあるのでこれで事足ります。
- https://github.com/guilhermechapiewski/simple-db-migrate
- http://guilhermechapiewski.github.io/simple-db-migrate/
と言いつつネット上に日本語の情報があまり無さそうだったので
補足がてら書いておきます。
インストール
$ pip install simple-db-migrate
これだけです。
準備
用意するのは
simple-db-migrate.conf
という設定ファイル- 各.migrationファイル
だけです。※コマンドラインオプションでも対応できるので設定ファイルは必須ではないですが。
公式にも例があるので、参考にすると良いと思います。
https://github.com/guilhermechapiewski/simple-db-migrate/tree/master/example/mysql
使い方
下記のような設定ファイルを用意します。
simple-db-migrate.conf
DATABASE_HOST = os.getenv("DATABASE_HOST") or "localhost" DATABASE_USER = os.getenv("DATABASE_USER") or "root" DATABASE_PASSWORD = os.getenv("DATABASE_PASSWORD") or "" DATABASE_NAME = os.getenv("DATABASE_NAME") or "migration_example" DATABASE_MIGRATIONS_DIR = os.getenv("MIG_DIR") or "."
下記のコマンドで.migrationファイルを生成します。
$ db-migrate --create create_table_users Starting DB migration on host/database "localhost/migration_example" with user "root"... - Created file '/Users/innossh/tmp/simple-db-migrate/20141025203258_create_table_users.migration' Done.
create_table_users
の部分は、.migrationの命名に利用されます。
生成された.migrationファイルを編集します。
下記のようにしました。
20141025203258_create_table_users.migration
#-*- coding:utf-8 -*- SQL_UP = u""" CREATE TABLE users ( id int(11) NOT NULL auto_increment, oid varchar(500) default NULL, first_name varchar(255) default NULL, last_name varchar(255) default NULL, email varchar(255) default NULL, PRIMARY KEY (id) ); """ SQL_DOWN = u""" DROP TABLE users; """
では、マイグレーションを実行してみましょう。
$ db-migrate Starting DB migration on host/database "localhost/migration_example" with user "root"... - Current version is: 0 - Destination version is: 20141025203258 Starting migration up! *** versions: ['20141025203258'] ===== executing 20141025203258_create_table_users.migration (up) ===== Done.
これでUPのSQLが実行できました。確認してみると
mysql> use migration_example Database changed mysql> show tables; +-----------------------------+ | Tables_in_migration_example | +-----------------------------+ | __db_version__ | | users | +-----------------------------+ 2 rows in set (0.00 sec) mysql> desc users; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | oid | varchar(500) | YES | | NULL | | | first_name | varchar(255) | YES | | NULL | | | last_name | varchar(255) | YES | | NULL | | | email | varchar(255) | YES | | NULL | | +------------+--------------+------+-----+---------+----------------+ 5 rows in set (0.00 sec)
ちゃんとusers
テーブルが作成されています。
ここで気になるのが__db_version__
テーブルですね。
mysql> select * from __db_version__ \G *************************** 1. row *************************** id: 1 version: 0 label: NULL name: NULL sql_up: NULL sql_down: NULL *************************** 2. row *************************** id: 2 version: 20141025203258 label: NULL name: 20141025203258_create_table_users.migration sql_up: CREATE TABLE users ( id int(11) NOT NULL auto_increment, oid varchar(500) default NULL, first_name varchar(255) default NULL, last_name varchar(255) default NULL, email varchar(255) default NULL, PRIMARY KEY (id) ); sql_down: DROP TABLE users; 2 rows in set (0.00 sec)
上記のようなレコードが追加されていました。
このテーブルでどのバージョンまでのマイグレーションが実行済みなのか判別しているようです。
ついでにDOWNも実行してみましょう。
$ db-migrate --migration=0 Starting DB migration on host/database "localhost/migration_example" with user "root"... - Current version is: 20141025203258 - Destination version is: 0 Starting migration down! *** versions: ['20141025203258'] ===== executing 20141025203258_create_table_users.migration (down) ===== Done.
mysqlを確認してみると
mysql> show tables; +-----------------------------+ | Tables_in_migration_example | +-----------------------------+ | __db_version__ | +-----------------------------+ 1 row in set (0.00 sec) mysql> select * from __db_version__ \G *************************** 1. row *************************** id: 1 version: 0 label: NULL name: NULL sql_up: NULL sql_down: NULL 1 row in set (0.00 sec)
users
テーブルはDOWNにより削除されて、__db_version__
テーブルが残っています。
このように、バージョン指定でマイグレーションを実行し
現在のバージョンとの差異でUPやDOWNのSQLが実行されるようになっています。便利!
ハマッたこと
- 日本語コメントが付いたSQLを実行しようとするとエラーになってしまった
import sys; sys.setdefaultencoding('utf-8')
と記述されたsitecustomize.py
ファイルを
Pythonインストールディレクトリのsite-packages配下に置いたらエラー解消した
- 複数DBスキーマをいっぺんに扱いたかったが、ワークアラウンドな感じになってしまった
複数環境(develop,staging,productionなど)にも対応していたので
わりと問題なく使えそうでした。
とはいえ複数人で利用するとやはり競合(ファイルじゃなくてバージョンが)が起きてしまうので難しいですね。
良い案を思いついたら自分で作ってみたい。