【Rails】エラー対処法:This migration uses change_column, which is not automatically reversible. StandardError: An error has occurred, this and all later migrations canceled:

rails-prograshi(プロぐらし)-kv Rails
記事内に広告が含まれていることがあります。
[PR]

Railsでロールバック(rollback)を実行したときに、マイグレーションファイルの中のchangeメソッドでchange_columnを定義していると、次のようなエラーが発生します。

この原因と対処法について。

エラー例

rails db:rollback STEP=2

== 20210724085421 ChangeClientsAgeToInteger2: reverting =======================
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.

/rails-vue/db/migrate/20210724085421_change_clients_age_to_integer2.rb:3:in change' /rails-vue/bin/rails:5:in
/rails-vue/bin/spring:10:in block in <top (required)>' /rails-vue/bin/spring:7:intap’
/rails-vue/bin/spring:7:in `’

Caused by:
ActiveRecord::IrreversibleMigration:

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.

/rails-vue/db/migrate/20210724085421_change_clients_age_to_integer2.rb:3:in change' /rails-vue/bin/rails:5:in
/rails-vue/bin/spring:10:in block in <top (required)>' /rails-vue/bin/spring:7:intap’
/rails-vue/bin/spring:7:in `’
Tasks: TOP => db:rollback
(See full trace by running task with –trace)


原因

通常、 ロールバック(rollback) を実行すると、マイグレーションファイルのchangeメソッドと逆の処理を自動で行ってくれます

ですが、この自動逆戻しが使えるのは、一部の限られたマイグレーション定義のみです。

カラムの型を変更する、change_columnはchangeメソッドによる自動逆戻しができないため、このエラーが発生しています。

class ChangeClientsAgeToInteger2 < ActiveRecord::Migration[6.1]
  def change
    change_column :clients, :age, :integer, using: "age::integer"
  end
end

このマイグレーションファイルの内容はclietnsテーブルのageカラムの型をintegerに変更する処理です。


対処法

対処法は2つあります。chnageメソッドを次のメソッドに変更して、ロールバック時の処理を明記します。

  1. upメソッドとdownメソッドを使う
  2. 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.

upメソッドとdownメソッドを使う

upメソッドとdownメソッドを使って修正する場合は、changeをupに変更し、downメソッドにロールバック時に実行する処理を記述します。

マイグレーションファイルの書き方は次のとおりです。

class <クラス名> < ActiveRecord::Migration[6.1]
  def up
    <マイグレーション実行時の処理>
  end

  def down
    <ロールバック実行時の処理>
  end
end
注意点

changeをupに変更して、downを記載しなくてもエラーが発生することなくロールバックをすることができます。

ですが、これは戻す処理を何もしていないので、ロールバック前後でテーブルの構造が変化しません。

意図的にdownの処理を書かないことはありますが、テーブルのスキーマを元の状態に戻すためにロールバックを行うのであれば、downメソッドも記述する必要があります。


マイグレーションファイルを修正した後で、ロールバックを実行します。

処理が正しく完了したら、対象のマイグレーションファイルを手動で削除します。


実例

次のようなエラーが発生するマイグレーションファイルを書き変えます。

内容はclietnsテーブルのageカラムの型をintegerに変更する処理です。

class ChangeClientsAgeToInteger2 < ActiveRecord::Migration[6.1]
  def change
    change_column :clients, :age, :integer, using: "age::integer"
  end
end


 ↓ 修正後

ageカラムの元の型はstringなので、カラムの型をstringに戻す処理をdownに記載します。

class ChangeClientsAgeToInteger2 < ActiveRecord::Migration[6.1]
  def up
    change_column :clients, :age, :integer, using: "age::integer"
  end

  def down
    change_column :clients, :age, :string
  end
end

型の種類によってusingを使った指定が必要になります。詳細はこちらをご参考ください。

マイグレーションの実行

マイグレーションファイルの修正が完了したので、rails db:migrateを実行します。

rails-vue# rails db:rollback
== 20210724085421 ChangeClientsAgeToInteger2: reverting =======================
-- change_column(:clients, :age, :string)
   -> 0.0451s
== 20210724085421 ChangeClientsAgeToInteger2: reverted (0.0452s) ==============


エラーの発生なくマイグレーションを行うことができました。

change_column(:clients, :age, :string) とあり、指定したカラムの型もstringに戻っていることがわかります。

この状態になれば、対象のマイグレーションファイルを削除できます


なお、マイグレーションファイルを削除せず、再度マイグレーションを実行した場合は、ageカラムの型をintegerに戻すことができます。

# rails db:migrate
== 20210724085421 ChangeClientsAgeToInteger2: migrating =======================
-- change_column(:clients, :age, :integer, {:using=>"age::integer"})
   -> 0.0404s
== 20210724085421 ChangeClientsAgeToInteger2: migrated (0.0407s) ==============


up&downとreversibleの違いは何か?

up&downメソッドとreversibleメソッドの違いは、書き方の違いだけで処理の記述は同じです。upとdownをひとまとめにしたのがreversibleになります。

例えば、テーブルへのカラム追加やインデックス付与、その逆の削除などをまとめて指定できるchange_tabeをupとdownで記述すると次のようになります。

class ChangeProductsPrice < ActiveRecord::Migration[5.0]
  def up
    change_table :products do |t|
      t.change :price, :string
    end
  end

  def down
    change_table :products do |t|
      t.change :price, :integer
    end
  end
end


これをreversibleで書くと次のようになります。どちらも同じ処理です。

reversibleは dirという変数を作成し、その変数に対してup, downメソッドを実行し、{ 処理 } を記述します。

class ChangeProductsPrice < ActiveRecord::Migration[5.0]
  def change
    reversible do |dir|
      change_table :products do |t|
        dir.up   { t.change :price, :string }
        dir.down { t.change :price, :integer }
      end
    end
  end
end


reversibleメソッドを使う

reversibleメソッドの書き方は次のようになります。

class <クラス名> < ActiveRecord::Migration[5.0]
  def change
    reversible do |dir|
        dir.up   { マイグレーション時の処理 }
        dir.down { ロールバック時の処理 }
      end
    end
  end
end

reversible複雑な処理を実行するときに使います。changeメソッドと併記してreversibleを記述することもできます。

マイグレーション時は上から順に処理を実行して、ロールバック時は下から順に処理を実行します。reversibleメソッドのところでは、upブロックとdownブロックの一方を実行します。

class ExampleMigration < ActiveRecord::Migration[5.0]
  def change
    create_table :distributors do |t|
      t.string :zipcode
    end

    reversible do |dir|
      dir.up do
        # CHECK制約を追加
        execute <<-SQL
          ALTER TABLE distributors
            ADD CONSTRAINT zipchk
              CHECK (char_length(zipcode) = 5) NO INHERIT;
        SQL
      end
      dir.down do
        execute <<-SQL
          ALTER TABLE distributors
            DROP CONSTRAINT zipchk
        SQL
      end
    end

    add_column :users, :home_page_url, :string
    rename_column :users, :email, :email_address
  end
end

マイグレーション時の流れ

  1. change(zipcodeのカラム追加)
  2. reversibleのup(CHECK制約を追加)
  3. add_column(home_page_urlカラムを追加)
  4. rename_column(emailカラムをemail_addressに変更)

ロールバック時の流れ

  1. rename_column(email_addressカラムをemailに変更)
  2. remove_column (home_page_urlカラムを削除)
  3. reversibleのdown(制約を削除)
  4. drop_table(zipchkカラムを削除)

(参考)Rails公式 Active Record マイグレーション reversibleを使う


changeメソッドが使える定義一覧

ロールバック(rollback)を実行したときに、changeメソッドで自動逆戻し可能なマイグレーション定義は次になります。

基本的には、add_〇〇に対して、逆となるremove_〇〇 が用意されている定義など、機械的に逆処理が行えるもののみ該当します。

定義内容実例
add_columnカラムを追加add_column :users, :picture, :binary, limit: 2.megabytes
add_foreign_key指定したテーブルに外部キー制約を追加add_foreign_key :articles, :authors
add_index指定したテーブルにインデックスを追加add_index :users, :name
add_reference既存のテーブルにリファレンスを追加add_reference(:products, :supplier, index: { unique: true })
add_itemstampsタイムスタンプを追加add_timestamps :suppliers, null: true
change_column_defaultカラムの初期値を設定 (:fromと:toの指定は省略できない)change_column_default(:suppliers, :qualification, ‘new’)
chnage_column_nullNULL制約の追加または削除change_column_null(:users, :nickname, false)
create_join_table2つのテーブルを結合して新しいテーブルを作成create_join_table(:assemblies, :parts, options: ‘ENGINE=InnoDB DEFAULT CHARSET=utf8’)
create_tableテーブルを作成create_table :suppliers, options: ‘ENGINE=InnoDB DEFAULT CHARSET=utf8’
drop_join_table結合テーブルを削除drop_join_table(:assemblies, :parts)
drop_table指定したテーブルを削除drop_table :products
remove_column指定したテーブルのカラムを削除(型の指定必須)remove_column :users, :description, :string, limit: 20
remove_foreign_key外部キーの削除(2番目のテーブルを指定しなければならない)remove_foreign_key :accounts, column: :owner_id
remove_index指定したテーブルのインデックスを削除remove_index :accounts, column: :branch_id
remove_reference既存のテーブルのリファレンスを削除remove_reference(:products, :user, index: true)
remove_timestamps既存のテーブルのcreated_atとupdated_atを削除remove_timestamps(:users)
rename_column指定したテーブルのカラム名を変更rename_column :suppliers, :description, :name
rename_index指定したテーブルのインデックスを変更rename_index :people, ‘index_people_on_last_name’, ‘index_users_on_last_name’
rename_table指定したテーブルの名前を変更rename_table :now_table, :new_table

ブロックでchange、change_default、removeが呼び出されない限り、change_tableもロールバック可能です。

remove_columnは、3番目の引数でカラムの型を指定すればロールバック可能になります。元々のカラムオプションも指定しておかないと、ロールバック時にRailsがカラムを再作成できなくなります。

https://railsguides.jp/active_record_migrations.html


また、命名規則に沿って、rails g migration を行ったときに、自動でchangeメソッドが記述されるものは、ロールバック時に逆戻しができます。

(参考)【Rails】テーブルやカラムの追加・名前の変更・削除・データ型を変更する方法を実例で解説


タイトルとURLをコピーしました