PyramidでToDo管理サービスを作る 【DBマイグレーション】

データベースを使用するためのプロジェクトもできたことだし、これからToDo管理サービスで使うモデルを作成していこうと思う。

実行環境

モデルを作る

ToDoManager/todomanager/models.py にscaffold時に作成されたデフォルトのモデルが設定されている。

class MyModel(Base):
    __tablename__ = 'models'
    id = Column(Integer, primary_key=True)
    name = Column(Text)
    value = Column(Integer)

Index('my_index', MyModel.name, unique=True, mysql_length=255)

今回は不要なので削除しつつ、必要となるモデルを追加していく。
取り敢えずユーザ情報とかは後回しにして、ToDoを管理するためのモデルを作成する。

テーブル定義

ひとまずは必要最低限のものに絞ってリストアップする。

  • ToDo情報
    • ID
    • ToDo内容
    • 登録日
    • 更新日

上の内容をコードにするとこんな感じになる。
必要な import は物を適宜追加する。

class Task(Base):
    __tablename__ = 'tasks'
    id = Column(Integer, primary_key=True)
    summary = Column(Text)
    created_at = Column(DateTime, default=datetime.datetime.now())
    updated_at = Column()

DB初期化用のファイル script/initializedb.py には先ほど削除した MyModel が残っているため該当部分を削除する。
また、初期値として登録するものはないためその部分も削除する。

from ..models import (
    DBSession,
    MyModel,  <- 削除する
    Base,
    )

def main(argv=sys.argv):
    (省略)
    with transaction.manager:
        model = MyModel(name='one', value=1)  <- 削除する
        DBSession.add(model)                  <- 削除する
        pass                                  <- 追加する

データベースのマイグレーション

alembicのインストール

Djangoとは異なり、マイグレーションをするに辺りそれ用のモジュールをインストールする必要があるらしい。
マイグレーション用モジュールとして alembic をインストールする。

(pyramid_python)> pip install alembic

インストール後の pip list は以下のようになる。
特にバージョン番号を指定していないから、インストールする度にバージョン番号が変わる。

(pyramid_python)> pip list
alembic (0.8.2)
chameleon (2.22)
mako (1.0.1)
markupsafe (0.23)
PasteDeploy (1.5.2)
pip (7.1.2)
pygments (2.0.2)
pyramid (1.5.7)
pyramid-chameleon (0.3)
pyramid-debugtoolbar (2.4)
pyramid-mako (1.0.2)
pyramid-tm (0.12)
python-editor (0.4)
repoze.lru (0.6)
setuptools (12.0.5)
sqlalchemy (1.0.8)
ToDoManager (0.0, e:\developments\pycharmprojects\todomanager\todomanager)
transaction (1.4.4)
translationstring (1.3)
venusian (1.0)
waitress (0.8.9)
WebOb (1.4.1)
zope.deprecation (4.1.2)
zope.interface (4.1.2)
zope.sqlalchemy (0.7.6)

alembic を使うために次のコマンドで初期化する。

(pyramid_python)> alembic init alembic

こんな感じのログが出力されて必要なファイルが生成される。

Creating directory E:\Developments\PycharmProjects\ToDoManager\ToDoManager\alembic ... done
Creating directory E:\Developments\PycharmProjects\ToDoManager\ToDoManager\alembic\versions ... done
Generating E:\Developments\PycharmProjects\ToDoManager\ToDoManager\alembic.ini ... done
Generating E:\Developments\PycharmProjects\ToDoManager\ToDoManager\alembic\env.py ... done
Generating E:\Developments\PycharmProjects\ToDoManager\ToDoManager\alembic\README ... done
Generating E:\Developments\PycharmProjects\ToDoManager\ToDoManager\alembic\script.py.mako ... done
Please edit configuration/connection/logging settings in 'E:\\Developments\\PycharmProjects\\ToDoManager\\ToDoManager\\alembic.ini' before proceeding.

alembicの設定

生成された alembic.inisqlalchemy.url を変更する。
今はSQLiteを使用しているので以下のように書き換える。

sqlalchemy.url = driver://user:pass@localhost/dbname
↓
sqlalchemy.url = sqlite:///%(here)s/ToDoManager.sqlite

サーバを起動させるために作成したテーブルなどを一度削除する。
SQLiteなので今回は手っ取り早く ToDoManager.sqlite ファイルを削除する。

alembic/env.py#add your model's MetaData object here とコメントが書かれている辺りに次のコードを追記する。

from todomanager.models import Base
target_metadata = Base.metadata

マイグレーションファイルの生成

テーブルの状態と models.py の差分で自動的にマイグレーションファイルを生成するモードがあるのでそれを利用する。

(pyramid_python)> alembic revision --autogenerate -m "Create Task model"

こんな感じのログが出力されて、マイグレーションに必要なファイルが生成される。

INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'tasks'
Generating E:\Developments\PycharmProjects\ToDoManager\ToDoManager\alembic\versions\809bd8fbc9_create_task_model.py ... done

生成されたマイグレーションファイルファイルはこんな感じになる。
リビジョン番号は、マイグレーションファイルを識別するためのコードのため、ファイルを生成する度に異なる。

# revision identifiers, used by Alembic.
revision = '809bd8fbc9'
down_revision = None
branch_labels = None
depends_on = None

from alembic import op
import sqlalchemy as sa


def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.create_table('tasks',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('summary', sa.Text(), nullable=True),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    ### end Alembic commands ###


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('tasks')
    ### end Alembic commands ###

ドキュメントを見ると、マイグレーションファイルの自動生成する際に、変更を検知できる項目とできない項目があるらしい。

変更を検知できるもの

  • テーブルの追加と削除
  • カラムの追加と削除
  • カラムのnullステータス
  • インデックスやユニーク制約などの基本的な変更
  • 外部参照キーの基本的な変更

変更を検知できないもの

  • テーブル名の変更
  • カラム名の変更
  • 無名のインデックスなど

詳細はここ Auto Generating Migrations で確認できる。

マイグレーションの実行

生成されたマイグレーションファイルを使用してデータベースのマイグレーションをする。

  • 状態を確認する

    alembic current コマンドを入力して状態を確認する。

      (pyramid_python)> alembic current
      INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
      INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
    
  • マイグレーションを実行する

    alembic upgrade header コマンドでマイグレーションを実行する。

      (pyramid_python)> alembic upgrade head
      INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
      INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
      INFO  [alembic.runtime.migration] Running upgrade  -> 809bd8fbc9, Create Task model
    

    upgrade には、 リビジョン番号head が指定できる。 それ外にも +N 表記でも可能らしい。

  • マイグレーションの実行を取り消す

    alembic downgrade -1 コマンドでマイグレーションを取り消せる。

      (pyramid_python)> alembic downgrade -1
      INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
      INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
      INFO  [alembic.runtime.migration] Running downgrade 809bd8fbc9 -> , Create Task model
    

    downgrade には、 リビジョン番号-N が指定できる。

  • マイグレーションの履歴を確認する

    alembic history コマンドでマイグレーションの履歴を確認できる。

      (pyramid_python)> alembic history
      <base> -> 809bd8fbc9 (head), Create Task model
    

(追記)
上記の変更を反映したコードをコミット。

github.com

view.py について変更し忘れていたため、修正したコミット。

github.com