読者です 読者をやめる 読者になる 読者になる

NissyBlog

Life goes on.

Djangoでマイグレーション実行時のオプションについて調べてみた

Djangoスキーママイグレーションを実行した時にオプションについて調べたことを
書いてみようと思います。

Djangoでのマイグレーション実行時の流れ

本題に入る前に、Djangoマイグレーションを実行する流れについて簡単にまとめてみます。
と言っても、manage.pyを使って下記二つのコマンドを実行するだけで問題ありません。

  1. makemigrationsコマンドを実行して、マイグレーションファイルを作成
  2. sqlmigrateコマンドを実行して、マイグレーションのためのSQLを作成
  3. migrateコマンドを実行して、マイグレーションを実行

上記のような流れでマイグレーションを実行します。

migrateコマンド実行時のオプション

一番最後のmigrateコマンド実行のために、
Djangoのドキュメントを見ていると、マイグレート実行時に三つの実行方法があると書かれています。

以上の三つの実行方法が選択出来ます。

ドキュメントを読んでみた

それぞれがどのような挙動をしているのかをドキュメントで確認してみます。

何もオプションを付けずに実行する場合

まずは、何もオプションを付けずに実行する場合、

No arguments: All migrated apps have all of their migrations run, and all unmigrated apps are synchronized with the database,

実行済のマイグレーションはそのまま実行され、実行されていないマイグレーションをDBに適用する。
と読み取れます。

特に変哲のないマイグレーション実行のようです。
ちなみに、どのアプリケーションに対して実行するかや、
どのDBに対して実行するかも指定出来るようです。

fakeオプションを付けてマイグレーションを実行する場合

The --fake option tells Django to mark the migrations as having been applied or unapplied, but without actually running the SQL to change your database schema.

全てのマイグレーションは実行されず、DBのスキーマに対する変更も行われない。
と読み取れます。
文字通りのfakeオプション実行です。

fake-initialオプションを付けてマイグレーションを実行する場合

New in Django 1.8.

こちらは1.8から追加されたオプションのようです。

The --fake-initial option can be used to allow Django to skip an app’s initial migration if all database tables with the names of all models created by all CreateModel operations in that migration already exist.

CreateModelで作られた全てのアプリケーションのinitialマイグレーションは適用されない。
と読み取れます。

発生した疑問

ここで、二つの疑問が生じました。

  • fakeオプションとfake-initialオプションはどんな時に使い分けたらいいのでしょうか。
  • そもそも、1.8からfake-initialオプションが追加されたのはなぜでしょうか。
実行して比較する

適当なサンプルプロジェクトを立ち上げ、両方のコマンドを実行し、その結果を比較してみることにしました。

fakeオプション

$ python manage.py migrate --fake
Operations to perform:
  Synchronize unmigrated apps: staticfiles, debug_toolbar, messages
  Apply all migrations: test, contenttypes, sessions
Synchronizing apps without migrations:
  Creating tables...
    Running deferred SQL...
  Installing custom SQL...
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... FAKED
  Applying contenttypes.0002_remove_content_type_name... FAKED
  Applying sessions.0001_initial... FAKED

fake-initialオプション

$ python manage.py migrate --fake-initial
Operations to perform:
  Synchronize unmigrated apps: staticfiles, debug_toolbar, messages
  Apply all migrations: test, contenttypes, sessions
Synchronizing apps without migrations:
  Creating tables...
    Running deferred SQL...
  Installing custom SQL...
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... FAKED
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying sessions.0001_initial... FAKED

両方の動きに違いが見られました。

Djangoのソースを見てみる

0002_remove_content_type_nameというマイグレーションが実行されなかったため、
0002_remove_content_type_name.pyというファイルを探してみると、
test_env/lib/python2.7/site-packages/django/contrib/contenttypes/migrations/0002_remove_content_type_name.py
という箇所に0002_remove_content_type_name.pyというファイルは存在することが分かりました。
どうやらDjangoの標準のテーブルへの変更を記述しているマイグレーションファイルのようです。

スキーママイグレーション機能がDjangoに追加されたのは1.7からであること。
fake-initialオプションがDjango1.8から追加されたこと。
この二点には何か関係があると考えたので、
Django1.7とDjango1.8のソースコードを比較してみることにしました。

Django1.7のソースコードを見てみると、
Django自体のマイグレーションファイルは0001_initialしか持っていないようですが、
Django1.8のソースコードを見てみると、
Djangoの標準のテーブルへの変更は新しいマイグレーションファイルで適用しているようです。

どうやらfake-initialはDjango1.8以降のDjangoの標準のテーブルへの変更も反映させるために
追加されたオプションのようであるということが分かりました。

調査結果をまとめる

1.7の時には
Django側では0001_initialのmigrationファイルしか存在しませんでした。
そのため、fakeのみで対応することが出来ていました。
しかし、1.7から1.8にバージョンアップする際にDjangoの標準のテーブルに変更が入りました。
そのため、変更箇所がDjangoマイグレーションファイルに記述され、
Djangoソースコードに反映されたようです。

上記より、
fake-initialを実行させないとDjango1.8でアプリケーションを動かすため
に必要なテーブルの変更が反映されないため、
Django1.8以降のアプリケーションの場合、
fake-initialオプションを付けて実行した方がよさそうな印象です。

使いどころを考える

しかし、fake-initialオプションの使い所はどこなのでしょうか。
そもそも、オプション無しのマイグレーション実行でも問題ないように思えます。

そこで、マイグレーションのドキュメントを読み進めてみると、
Upgrading from Southという項目を見つけました。

どうやら、Django1.7のリリースノートによると、
スキーママイグレーション機能がDjangoに追加された時に、
Southという外部モジュールからのアップデートが可能となったようです。

この、Southという外部モジュールはDjango1.6まではスキーママイグレーションを実行するために
よく用いられていた外部モジュールですが、

Please note that South is now end of lifed in favour of the ​new migrations framework in Django 1.7, which is based on South but with significant design improvements. South will not work with Django 1.7; it supports only versions 1.4, 1.5 and 1.6.

上記の通り、Django1.7以降のバージョンではDjangoスキーママイグレーション機能の追加によって
使えなくなってしまったようです。

このSouthからのバージョンアップ時に実行するコマンドが
Django1.7のドキュメントでは下記のように記載されていますが、

Run python manage.py migrate. Django will see that the tables for the initial migrations already exist and mark them as applied without running them. (Only matching table names are checked; not their full schema - it’s up to you to make sure the existing schema is up to date with your models!)

Django1.8ではこのように記載されています

Run python manage.py migrate --fake-initial. Django will see that the tables for the initial migrations already exist and mark them as applied without running them. (Django won’t check that the table schema match your models, just that the right table names exist).

どうやらSouthを使うことが出来るバージョン(Django1.6以前)のバージョンからDjango1.8への
バージョンアップを行う時にfake-initialオプションによって
Djangoの標準のテーブルへの変更も取り込む必要があるようです。

まとめ

fakeオプションを付けてマイグレーションを実行した場合は一切、テーブルの変更は行われないですが、
fake-initialオプションを付けてマイグレーションを実行した場合は
Djangoが必要としている標準のテーブルへの変更が行われるようです。
fake-initialオプションを付けてマイグレーションを実行する場合は
Django1.6以前のバージョンからのバージョンアップなどといった場合に用いられる可能性が高いようです。