問題と原因
Rails3.1のマイグレーションでちょっとハマったのでメモしておきます。
マイグレーションのdownを実行する場合は
rake db:migrate:down VERSION=20120324025500
のような形で実行できます。
しかし、VERSIONで指定したバージョンがRailsが管理している現在のスキーマのバージョンよりも新しいと何も実行されないようです。
単純な例を示すとこんな感じです。
$ rails c >ActiveRecord::Migrator.current_version =>1 (実際は14桁) >quit $rake db:migrate:down VERSION=2 (何も起きない) $
上の例では現在のバージョン(=1)よりもdownで指定したバージョン(=2)の方が新しいので何も実行されません。
VERSIONを指定すれば強制的に実行できると思っていたのですが、実際そうではありませんでした。
まあ普通に使っているとそういうことは起きないんでしょうが、僕の場合マイグレーションの途中でエラーが出たりしたせいか、DBのスキーマとRailsが管理しているスキーマのバージョンがズレてしまいました。
その時の状況を単純化するとこんな感じです。
# db/migrate/2_add_foo.rb (スクリプトのバージョンは2) class AddFoo < ActiveRecord::Migration def change # Rails上のバージョンは1だが、同期がズレて実際のDBにはfoo列が存在している add_column :users, :foo, :string end end
いったんDB上のテーブルからfoo列を削除して、もう一度マイグレーションしたかったのですが、前述のように「$rake db:migrate:down VERSION=2」では何も起きません。
なのでこの後に「$rake db:migrate」しても「列名が重複しているのでエラー」と怒られるだけでした。
解決策
仕方がないので苦肉の策でRailsをダマすことにしました。
# db/migrate/2_add_foo.rb class AddFoo < ActiveRecord::Migration def change # 一時的にコメントアウト # add_column :users, :foo, :string end end
このようにDBへの操作を何もしないことにして、「$rake db:migrate」を実行します。
するとRailsが管理しているバージョンだけが2に上がります。foo列は存在したままです。
続いてさっきのコメントを外します。
# db/migrate/2_add_foo.rb class AddFoo < ActiveRecord::Migration def change # 元に戻す add_column :users, :foo, :string end end
ここで終わっても理屈的には問題ないのですが、念のためマイグレーションが正しく動作するか確認しておきます。
$rake db:migrate:redo
これでエラーなく列の削除と追加が実行できていれば成功です。
Railsと実際のDBのバージョンがズレてしまった場合に参考にしてみてください。