migrationファイルの「change」と「up、down」

こんにちは!

4月に入社した新人エンジニアのodaです。

Ruby on Railsを使ってアプリケーション開発をされている方は、マイグレーション機能を使ってテーブル定義に変更を加えているかと思います。

私自身、個人でRailsアプリを作成しているときは、もちろん、マイグレーション機能を使っていたのですが、ロールバックを意識することがなく、マイグレーションファイルの「change」と「up・down」の違いがわかっていませんでした。

今回は、この点について、掘り下げてみたいと思います。

(以下、Rails 7.0.2.3を使用しています。)


まずは、次のコマンドにより、Usersテーブルを作成します。

bin/rails g model Users name:string age:integer note:string


すると、次のようなマイグレーションファイルが作成されます。

class CreateUsers < ActiveRecord::Migration[7.0]
  def change
    create_table :users do |t|
      t.string :name
      t.integer :age
      t.string :note

      t.timestamps
    end
  end
end


このマイグレーションファイルを次のコマンドにより実行するとname、age、noteのカラムを持ったUsersテーブルが作成されます。

bin/rails db:migrate


さて、次のコマンドを実行するとどうなるでしょうか?

bin/rails db:rollback


直前に行ったマイグレーションがロールバックされ、Usersテーブルが削除されます。

このとき、Railsは、changeメソッドを逆転実行するとどうなるかを自動的に判断してくれています。(「テーブルの追加」の逆は「テーブルの削除」)


次にnoteのデータ型をstringからtextに変更するために、以下のマイグレーションファイルを作成、実行します。

class ChangeDatatypeNoteOfUsers < ActiveRecord::Migration[7.0]
  def change
    change_column :users, :note, :text
  end
end


すると、noteのデータ型はstringからtextへ変更されます。

この時、先ほどのrollbackコマンドを実行するとどうなるでしょうか?

次のようなエラーメッセージが出るかと思います。

rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:

This migration uses change_column, which is not automatically reversible.
To make the migration reversible you can either:
1. Define #up and #down methods in place of the #change method.
2. Use the #reversible method to define reversible behavior.

要約すると、change_columnを使用したマイグレーションは自動的には元に戻せないため、1もしくは2の方法をとってくださいと書かれています。

それでは、方法1を使って、先ほどのマイグレーションファイルを次のように書き換えてみます。upメソッドにはマイグレーション時の処理を、downメソッドにはロールバック時の処理を書きます。

class ChangeDatatypeNoteOfUsers < ActiveRecord::Migration[7.0]
  def up
    change_column :users, :note, :text
  end

  def down
    change_column :users, :note, :string
  end
end

すると、無事、ロールバックをすることができるようになります。

また、方法2を使うと、次のようにマイグレーションファイルを書き換えることもできます。

class ChangeDatatypeNoteOfUsers < ActiveRecord::Migration[7.0]
  def change
    reversible do |dir|
      change_table :users do |t|
        dir.up   { t.change :note, :text }
        dir.down { t.change :note, :string }
      end
    end
  end
end

マイグレーションファイルを使うときは、上記を意識することで、スムーズに開発を進めていきたいと思います。

ちなみに、changeメソッドが逆転実行できるのは、add_columnchange_column_nullcreate_tablerename_columnなど、あらかじめ決められたメソッドに限られています。

みなさんも、マイグレーション機能により、テーブル定義に変更を加えるときは、ぜひ、意識してみてください!