Top > テクノロジー > プログラミング

pythonでのマイグレーションにsqlalchemy-migrateを使ってみた






pythonプロジェクト作成中にマイグレーションツールの選定に困ったことはありませんか?

もしあなたのプロジェクトがsqlalchemyを使っているのならば、sqlalchemy-migrateを使ってみてはどうでしょう?

sqlalchemyと同じ文法で利用できるのですごく簡単ですよ!

DBのマイグレーションツールは最初の設計の時点ですごく悩むことの一つです。

できるなら、使い慣れた形のマイグレーションツールを使いたいという方のほうが多いと思います。

本記事ではそんなマイグレーションツールの一つであるsqlalchemy-migrateというツールを紹介します。

 

ソースコードはこちら

 

sqlalchemy-migrateは、pythonの代表的なORマッパーの一つであるsqlalchemyの文法をそのまま踏襲したマイグレーションツールで、sqlalchemyを用いて開発している方にとっては文法理解などの話が少なくて済むおすすめのマイグレーションツールです。

 

本記事ではsqlalchemy-migrateの導入から初歩的なテーブル管理までを解説します。

 

導入してみる

 

インストールにはpipを使います。

 

$ pip install sqlalchemy-migrate

 

その後は、下記のコマンド一発で雛形ができます。

 

$ migrate create my_repository "Example project"

 

雛形の形は以下の様になっています。

 

my_repository/
├── README
├── __init__.py
├── __pycache__
│   └── __init__.cpython-34.pyc
├── manage.py
├── migrate.cfg
└── versions
    ├── __init__.py
    └── __pycache__
        └── __init__.cpython-34.pyc

次に、manage.pyを編集しましょう。

manage.pyには、データベースへの接続情報を記載します。

 

$ vi my_repository/manage.py

 

sqliteへの接続は以下のように記載します。

 

#!/usr/bin/env python
from migrate.versioning.shell import main

if __name__ == '__main__':
    main(debug='False', url='sqlite:///project.db my_repository', repository='.')

 

mysqlへの接続は以下のように記載します。(pymysqlの接続例です。)

 

#!/usr/bin/env python
from migrate.versioning.shell import main

if __name__ == '__main__':
    main(debug='False', url='mysql+pymysql://user:password@host/database?charset=utf8', repository='.')

 

次に、migrate.cfgを編集しましょう。

バージョン管理情報をするための情報を記載します。

 

$ vi migrate.cfg

 

基本的には、repository_idとversion_tableを編集すればOKです。

version_tableはいまどこまでスキーマを適用しているかの情報を格納するテーブルで、repository_idはテーブルに格納するための識別子です。

 

[db_settings]
# Used to identify which repository this database is versioned under.
# You can use the name of your project.
repository_id=my_repository

# The name of the database table used to track the schema version.
# This name shouldn't already be used by your project.
# If this is changed once a database is under version control, you'll need to
# change the table name in each database too.
version_table=migrate_version

# When committing a change script, Migrate will attempt to generate the
# sql for all supported databases; normally, if one of them fails - probably
# because you don't have that database installed - it is ignored and the
# commit continues, perhaps ending successfully.
# Databases in this list MUST compile successfully during a commit, or the
# entire commit will fail. List the databases your application will actually
# be using to ensure your updates to that database work properly.
# This must be a list; example: ['postgres','sqlite']
required_dbs=[]

# When creating new change scripts, Migrate will stamp the new script with
# a version number. By default this is latest_version + 1. You can set this
# to 'true' to tell Migrate to use the UTC timestamp instead.
use_timestamp_numbering=False

 

次に以下のコマンドでデータベースのバージョン管理用に初期化します。

 

python manage.py version_control

 

mysqlだと以下のようなテーブルができます。

また、sqliteでも似たようなものができるはずです。

 

mysql> select * from migrate_version;
+---------------+-----------------+---------+
| repository_id | repository_path | version |
+---------------+-----------------+---------+
| my_repository | .               |       0 |
+---------------+-----------------+---------+
1 row in set (0.00 sec)

 

以上で、準備編は終了です。

どうです。非常に簡単でしょう?

 

実際にDB管理をしてみる

 

まず、以下の構造のテーブルを作成してみましょう。ここからは全てmysqlで実践します。

 

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `title` (`title`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

 

まず、テーブル作成スクリプトを追加するためのコマンドを打ちます。

 

python manage.py script "Add user table"

 

そうすると、versionディレクトリ配下に「001_Add_user_table.py」というスクリプトが作成されています。

 

$ ls versions/
001_Add_user_table.py __init__.py           __pycache__

 

以下のように編集してみましょう。

テーブル定義はsqlalchemyでもよく見たことがあると思いますので、説明は省略します。

upgrade()では、upgradeコマンドを打ったときの処理を記載します。

downgrade()ではdowngradeコマンドを打ったときの処理を記載します。こちらは、upgradeの処理を打ち消すものを記載します。

以下のコードでは、upgradeメソッドではuserテーブルを作成し、downgradeメソッドではuserテーブルを削除しています。

 

from sqlalchemy import *
from migrate import *

meta = MetaData()

user = Table(
        'user', meta,
         Column('id',            Integer, primary_key=True, nullable=False),
         Column('title',         String(255), nullable=False, unique=True),
         Column('created_at',    DateTime, nullable=False),
         Column('updated_at',    DateTime),
)

def upgrade(migrate_engine):
    # Upgrade operations go here. Don't create your own engine; bind
    # migrate_engine to your metadata
    meta.bind = migrate_engine
    user.create()


def downgrade(migrate_engine):
    # Operations to reverse the above upgrade go here.
    meta.bind = migrate_engine
    user.drop()

 

記載しおわったら、まずはupgradeしてdowngradeを実行するテストコマンドを実行しましょう。

 

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

 

大丈夫だった場合は、upgradeで実際にテーブルを作成してみましょう。

 

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

 

さて、実際にDBの中身はどうなったでしょうか?

 

Database changed
mysql> show tables;
+-------------------------+
| Tables_in_my_repository |
+-------------------------+
| migrate_version         |
| user                    |
+-------------------------+
2 rows in set (0.00 sec)

mysql> show create table user;
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                                                     |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| user  | CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `title` (`title`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select * from migrate_version;
+-----------------+-----------------+---------+
| repository_id   | repository_path | version |
+-----------------+-----------------+---------+
| Example project | .               |       1 |
+-----------------+-----------------+---------+
1 row in set (0.00 sec)

 

migrate_versionがあがって1になって、想定通りuserテーブルができたのがわかりましたね。

 

さあ、次はこのテーブルに1行追加してみましょう。

 

$ python manage.py script "Alter status column"

 

次は、statusというカラムを追加してみましょう。

先程作成されたスクリプトに以下の処理を記載します。

 

from sqlalchemy import *
from migrate import *

meta = MetaData()

def upgrade(migrate_engine):
    # Upgrade operations go here. Don't create your own engine; bind
    # migrate_engine to your metadata
    meta.bind = migrate_engine
    user = Table('user', meta, autoload=True)
    status_c = Column('status', Integer,  nullable=False)
    status_c.create(user)


def downgrade(migrate_engine):
    # Operations to reverse the above upgrade go here.
    meta.bind = migrate_engine
    user = Table('user', meta, autoload=True)
    user.c.status.drop()

 

そして、最初の時と同じくupgradeで反映すると以下のようになります。

statusカラムが増えているのがわかりますね。

 

mysql> desc user;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| title      | varchar(255) | NO   | UNI | NULL    |                |
| created_at | datetime     | NO   |     | NULL    |                |
| updated_at | datetime     | YES  |     | NULL    |                |
| status     | int(11)      | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+

 

また、1つ前のバージョンに戻したいということもあると思います。

その際は、downgradeコマンドを使いましょう。

 

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

 

上記は、バージョン1まで戻すという意味のコマンドです。

今回の場合は、追加されたstatusカラムが除去されます。

 

 desc user;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| title      | varchar(255) | NO   | UNI | NULL    |                |
| created_at | datetime     | NO   |     | NULL    |                |
| updated_at | datetime     | YES  |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

 

 

ここまでが基本的なsqlalchemy-migrateの操作方法です。

上記の処理をひたすら繰り返せば、実際にDBのテーブル構成を管理できます。

 

更に詳しい情報はこちら

sqlalchemy-migrate manual -> https://sqlalchemy-migrate.readthedocs.io  

 

sqlalchemyを使用したことがあるなら非常に簡単だと思います。

是非、マイグレーションツールを使うならsqlalchemy-migrateを検討してみてください。


フォローして最新の情報をチェック!





おすすめ記事



人気記事ランキング



最新のお知らせ

2017年03月29日 FULL HOUSE公式メディアが就活アンサーと連携しました
2017年03月01日 株式会社FULL HOUSEが創業1年を迎えました!
2017年02月13日 TABI CHANNELが多言語対応しました
2017年01月09日 【祝】Petpedia のFBいいね数が1万を突破しました!
2017年01月01日 【謹賀新年】2017年もFULL HOUSEをよろしくお願い致します。

タグ一覧

転職(10)
面接(13)
採用(13)
アプリ(7)
iPhoneアプリ(6)
Androidアプリ(6)
ゲーム(5)
アニメ(5)
洋楽(4)
映画(4)
本(2)
トレーニング(1)