注意!!
このエントリの内容はRails3.1.3での実行結果を元にしています。
将来的にこの内容が最新のRailsの仕様と異なる可能性も高いので、トラブルシューティングの目的でこのエントリを参照する場合は使用中のRailsバージョンをよく確認してください。
Rails3.1からはマイグレーションでchangeというメソッドが導入されました。
以前はUpメソッドに変更用の処理を、Downメソッドにロールバック用の処理を書く必要があったのですが、changeメソッドを使うとUpに相当する記述だけで済みます。
あとはRailsが自動的にDownに相当する処理を考えてくれます。(つまり、コードがよりDRYになります)
# Ruby 3.0以前 class AddAgeToUsers < ActiveRecord::Migration def self.up add_column :users, :age, integer end def self.down remove_column :users, :age end end
# Ruby 3.1以降 class AddAgeToUsers < ActiveRecord::Migration def change # remove_columnに相当する処理はRailsが自動生成してくれる add_column :users, :age, integer end end
ただし、いくつもカラムを追加するのであれば、:usersと毎回書くのが面倒です。なので、次のように書けそうです。(というか、その方がよりDRYです)
class AddSomeColumnsToUsers < ActiveRecord::Migration def change change_table :users do |t| t.integer :age t.string :nickname end end end
実際これでも「rake db:migrate」はすんなり実行できてしまいます。
が!!
何かの理由でDownに相当するような処理を実行しようとするとエラーが発生します。
$ rake db:migrate:redo == AddSomeColumnsToUsers: reverting ============================================ rake aborted! An error has occurred, this and all later migrations canceled: ActiveRecord::IrreversibleMigration Tasks: TOP => db:rollback (See full trace by running task with --trace)
「ActiveRecord::IrreversibleMigration」は「逆の処理(つまりロールバック)を実行できないマイグレーション」であることを示しています。
そうなんです。change_tableメソッドを使うと、ロールバックができなくなってしまうんです。
ググってみると、同じ現象がStack Overflowで質問されていました。
ruby on rails - why IrreversibleMigration occurs? - Stack Overflow
それによると、Railsが自動的にロールバックしてくれるメソッドは以下の9つのメソッドに限定されるそうです。
change_tableメソッドは残念ながらここに含まれていません。
- add_column
- add_index
- add_timestamps
- create_table
- create_join_table
- remove_timestamps
- rename_column
- rename_index
- rename_table
というわけで、changeメソッドを使いたい場合はchange_tableではなく、add_columnメソッドを使ってカラムを追加します(この例の場合)。
DRYさは多少失われますが、Downメソッドに同じカラムを繰り返し書くことに比べればDRYだと思います。
class AddSomeColumnsToUsers < ActiveRecord::Migration def change add_column :users, :age, :integer add_column :users, :nickname, :string end end
きっとそのうちchange_tableメソッドも自動的なロールバックに対応してくれるんじゃないかな〜と思いますが、しばらくはchange_tableメソッドの出番が減りそうですね。