PythonのMigrationツールSouthのチュートリアルをやった

PythonのMigrationツールSouthのチュートリアルをやったときのメモ書きです
http://ae35.bitbucket.org/south-doc-ja/tutorial/index.html

最初のマイグレーション

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

python manage.py schemamigration southtut --initial

マイグレーション実行

python manage.py migrate southtut


このようなエラーになる場合はsyncdbを忘れている

django.db.utils.DatabaseError: no such table: south_migrationhistory

最初はsyncdbをしてsouth_migrationhistoryテーブルを作成しておく必要がある

モデルの変更があった場合

 ./manage.py schemamigration southtut --auto

migrationsパッケージに「add_field_追加したフィールド名.py」のようなファイルが作成される。
確かrailsのmigrationはファイル名を自分で指定しなければいけなかったので、この辺は楽かも。

デフォルト値の指定

モデルにnull=Falseなフィールドを追加している場合、デフォルト値をどうするか聞かれる

 ? The field 'Knight.shrubberies' does not have a default specified, yet is NOT NULL.
 ? Since you are adding this field, you MUST specify a default
 ? value to use for existing rows. Would you like to:
 ?  1. Quit now, and add a default to the field in models.py
 ?  2. Specify a one-off value to use for existing columns now

1を選べば何もせずに終了。モデルにデフォルト値の設定を書いて再度実行する場合は1。
2を選ぶと、Pythonプロンプトが表示され、デフォルト値の入力を促される。ここで入力したデフォルト値は既存のレコードにのみ適用されることになる。

既存のフィールドにuniq指定を追加

普通schemamigrationで検知してmigration用のファイルが作成される。

python manage.py schemamigration southtut --auto


ユニーク指定したフィールドが、すでに既存のレコードで重複している場合はどうなるか?

なんか色々エラーになります。まあ当然ですね。

$ ./manage.py migrate southtut
Running migrations for southtut:
 - Migrating forwards to 0004_auto__add_unique_knight_name.
 > southtut:0004_auto__add_unique_knight_name
FATAL ERROR - The following SQL query failed: CREATE UNIQUE INDEX "southtut_knight_name" ON "southtut_knight"("name");
The error was: indexed columns are not unique
 ! Error found during real run of migration! Aborting.

 ! Since you have a database that does not support running
 ! schema-altering statements in transactions, we have had 
 ! to leave it in an interim state between migrations.

! You *might* be able to recover with:
 ! The South developers regret this has happened, and would
 ! like to gently persuade you to consider a slightly
 ! easier-to-deal-with DBMS (one that supports DDL transactions)
 ! NOTE: The error which caused the migration to fail is further up.
Error in migration: southtut:0004_auto__add_unique_knight_name

自分で重複レコードを修正してmigrateをやり直せばちゃんと通ります

マイグレーションの一覧表示

$ python manage.py migrate --list

データマイグレーション


datamigrationにアプリ名とmigration名(例えばhash_passwords)を指定する

$ python manage.py datamigration southtut2 hash_passwords

datamigrationの場合は作成されたファイルにあるforwards関数に自分で行いたいデータ変更処理を書く
以下はチュートリアルにある例そのままだが、注意点はモデルクラスはormパッケージのものを使うこと。

def forwards(self, orm):
        import random, sha, string
        for user in orm.User.objects.all():
            user.password_salt = "".join([random.choice(string.letters) for i in range(8)])
            user.password_hash = sha.sha(user.password_salt + user.password).hexdigest()
            user.save()

不可逆な処理の場合はbackwords関数でエラーを発生させる

def backwards(self, orm):
    raise RuntimeError("Cannot reverse this migration.")

なんとなく感覚はつかめた