Railsで一度マイグレーションを実施してテーブルを作成した後に、内容を修正したい時、ロールバックという便利な機能が存在します。
ですが、このロールバックがどの情報をもとに、何を実行しているのかを理解していないと、マイグレーションファイルを消す順番を間違えて、ロールバックや再マイグレーションができないというエラーが発生するなど、大きな不具合に至ることがあります。
ここでは、ロールバックの処理が具体的に何をしているのかを解説しています。
この記事を読むことで、
- ロールバックが何をしているか?
- どのタイミングでマイグレーションファイルを消せばいいか(消してはいけないか)
- UPとDOWNの意味
- schema_migrationsテーブルの役割と使い方
がわかります。
ロールバック(rollback)関連のコマンド一覧
参考にロールバック関連のコマンド一覧をまとめておきます。
コマンド | 内容 | 実例 |
---|---|---|
db:rollback | 1つ前の状態に戻す | rails db:rollback |
db:rollback STEP=n | n個前の状態に戻す | rails db:rollback STEP=3 |
db:migrate | マイグレーション(DOWNをUPにする) | rails db:migrate |
db:migrate:status | マイグレーションの状態一覧を表示 | rails db:migrate:status |
db:migrate VERSION=xxx | 指定したバージョンまでマイグレーションを実行(指定したバージョンを含む) | rails db:migrate VERSION=20080906120000 |
db:migrate:up VERSION=xxx | 指定したバージョンのみマイグレーションを実行(upとchangeメソッドの実行) | rails db:migrate:up VERSION=20080906120000 |
db:migrate:down VERSION=xxx | 指定したバージョンのみロールバックを実行(downとchangeメソッドの逆を実行) | rails db:migrate:down VERSION=20080906120000 |
db:migrate:redo | ロールバックし、再度マイグレーションを行う (rails db:rollback db:migrate) | rails db:migrate:redo |
db:migrate:redo STEP=n | 指定したバージョンまでロールバックし、再度マイグレーションを行う (rails db:rollback STEP=2 db:migrate) | rails db:migrate:redo STEP=2 |
db:drop | DBを削除 | rails db:drop |
db:reset | DBのリセット。スキーマを作り直しダミーデータを投入する。 (rails db:drop db:setup) (rails db:drop db:create db:schema:load db:seed) | rails db:reset |
db:migrate:reset | データベースの初期化(中身をすべて削除) | rails db:migrate:reset |
(参考)Rails公式 Active Record マイグレーション
複数のDB利用時
DBが複数ある場合は、ロールバック対象のDBを指定する必要があります。
db:rollback、db:create以外のコマンドはDBを指定することも、しないこともできます。指定しない場合は全てのDBに対して処理を実施します。
コマンド | 内容 | 実例 |
---|---|---|
db:rollback:DB名 | 対象のDBを1つ前の状態に戻す | rails db:rollback:primary |
db:rollback:DB名 STEP=n | 対象のDBをn個前の状態に戻す | rails db:rollback:primary STEP=3 |
db:create:DB名 | 指定したDBを作成(現在の環境に) | rails db:create:clients |
db:migrate:DB名 | 指定したDBをマイグレーション(DOWNをUPにする) | rails db:migrate:primary |
db:migrate:DB名:status | 指定したDBのマイグレーションの状態一覧を表示 | db:migrate:primary:status |
(参考)Rails公式 Active Recordで複数のデータベース利用
ロールバックとは?
ロールバックとは、テーブルを昔の状態にもどす処理のことです。
マイグレーション(rails db:migrate
) を実行するとマイグレーションファイルに沿って、テーブルを作成したり、カラムを追加・変更・削除したりといった、DBやテーブルの操作を行います。
ロールバックを実行すると、DBやテーブルに行った処理を戻してなかったことにできます。マイグレーションファイルは残るので、再度マイグレーションを実行することもできます。
ロールバックの処理の流れ
ロールバックのコマンドの一つである、rails db:rollback
を実行すると、直近のマイグレーション済みの処理を、一つ戻すことができます。
処理の流れは次のようになっています。
- 直近のマイグレーションファイルの逆の処理を行う
- changeメソッドの場合はその逆
- downメソッドを実行
- schema_migrationsテーブルから対象のバージョンを削除
- マイグレーションのステータスをdownにする
直近のマイグレーションファイルの逆の処理を行う
テーブルに対して処理を行うには rails db:migrateを実行します。このとき、マイグレーション未実施のマイグレーションファイルの内容が実行され、DBに反映されます。
ロールバックでは、この逆の処理を行います。
rails g migrationでマイグレーションファイルを作成すると、changeメソッドを記述したファイルが生成されます。
class <クラス名> < ActiveRecord::Migration[6.1]
def change
end
end
changeメソッドとは?
changeメソッドとは、migrationの時にはそのメソッドを実行し、ロールバックの時には、その処理と逆の処理をRailsが自動で判定して行うものです。
ですが、ロールバック時の自動逆戻しが使えるのは、一部の限られたマイグレーション定義かつ、指定された条件に一致する場合のみです。
基本的には、add_〇〇に対して、逆となるremove_〇〇 が用意されている定義など、機械的に逆処理が行えるもののみ該当します。
詳細は【Rails】マイグレーションファイルでchangeメソッドが使える定義一覧表 をご参考ください。
upメソッドとdownメソッドとは?
upメソッドとdownメソッドは、マイグレーション時の処理とロールバック時の処理を明示したものです。
changeに対応していないマイグレーション定義は手動でこの形に書き変える必要があります。(他にもreversibleメソッドを使う方法があります)
class <クラス名> < ActiveRecord::Migration[6.1]
def up
<マイグレーション実行時の処理>
end
def down
<ロールバック実行時の処理>
end
end
schema_migrationsテーブルから対象のバージョンを削除
ロールバックを実行すると、schema_migrationsテーブルから対象のバージョンが削除されます。
これを理解するために、Railsがマイグレーションの状況をどのように管理しているかを解説します。
マイグレーションのバージョン(version)
rails g model
でモデルとマイグレーションファイルを生成すると、ファイル名に日付や時刻が入ります。これらは全て固有な値になります。
Railsは、この日付や時刻をバージョンとしてマイグレーションの状況を管理しています。
マイグレーションファイルの例
例えば、Userモデル(usersテーブル)を作成すると、次のようになります。
# rails g migration User name:string
Running via Spring preloader in process 835
invoke active_record
create db/migrate/20210725031548_user.rb
root@a3d49a8a5adf:/rails-vue# rails g model User name:string
Running via Spring preloader in process 845
invoke active_record
create db/migrate/20210725032240_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
20210725032240_create_users.rb というマイグレーションファイルが生成されます。この 20210725032240 がこのマイグレーションのバージョンになります。
マイグレーションファイルの中身は次のようになっています。
class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
t.string :name
t.timestamps
end
end
end
changeメソッドに自動で処理が記述されています。このように自動でchangeメソッドの処理が記述されている場合はロールバック可能です。
この時点では、DBにusersテーブルは追加されていません。
rails db:migrate
を実行すると、このマイグレーションファイルが処理され、DBにusersテーブルが追加されます。
マイグレーションの状況確認
どのマイグレーションファイルが実行されていて、未実施のマイグレーションファイルがどれかを確認するには、db:migrate
の後ろに :status
をつけて実行します。
rails db:migrate:status
すると、マイグレーションファイルの一覧が表示され、左端のstatusで状況を確認できます。
statusはupかdownで表示されます。
status | 内容 |
---|---|
up | マイグレーション済み(DBに反映済み) |
down | マイグレーション前(DBに反映していない) |
実例
# rails db:migrate:status
database: rails_vue_development
Status Migration ID Migration Name
--------------------------------------------------
up 20210724031048 Create clients
up 20210724031617 Add name to client
up 20210724031825 Add name to client2
up 20210724033923 Add age to client
up 20210724070410 Create students
up 20210724080344 Rename students to people
up 20210724082720 Rename name to client name in clients
up 20210724083954 Change clients age to integer
down 20210725032240 Create users
上記の例だと、20210725032240 Create users
のみがdownなので、このマイグレーションファイルの内容がDBに反映されていないことがわかります。
この状態で、rails db:migrateを実行すると、downとなっているマイグレーションが実行され、statusがupに変わります。
このupやdownの管理は、 schema_migrationsテーブル で行われています。
schema_migrationsテーブルとは?
schema_migrationsテーブルとは、マイグレーションの状況を管理するためのテーブルです。
マイグレーションを実施して、内容がDBに反映されると、 schema_migrationsテーブルにそのバージョンが追加されます。
schema_migrationsテーブルの例
Railsは、schema_migrationsテーブルと、マイグレーションファイルの状況を突き合わせて、対象のバージョンがどちらにも存在する場合はupを返します。schema_migrationsテーブルに存在しない場合はdownを返します。
ロールバックと schema_migrationsテーブルの関係
ロールバックを実行すると、schema_migrationsテーブルから対象のバージョンが削除されます。
試しに、 schema_migrationsテーブルが次のようになっている状態で rails db:rollback
を実行します。(No.12まであります)
↓ rails db:rollback
# rails db:rollback
== 20210724083954 ChangeClientsAgeToInteger: reverting ========================
-- change_column(:clients, :age, :string)
-> 0.0634s
== 20210724083954 ChangeClientsAgeToInteger: reverted (0.0636s) ===============
↓ schema_migrationsテーブルを更新
先ほどは、No12まであったものが、最新のバージョンが1つ削除され、No.11までになっています。
マイグレーションのステータスがdownになる
DBのschema_migrationsテーブルからバージョンが削除され、Railsのマイグレーションファイルと差が発生すると、マイグレーションのステータスがdownに変わります。
# rails db:migrate:status
database: rails_vue_development
Status Migration ID Migration Name
--------------------------------------------------
up 20210724031048 Create clients
up 20210724031617 Add name to client
up 20210724031825 Add name to client2
up 20210724033923 Add age to client
up 20210724070410 Create students
up 20210724070509 Create test camels
up 20210724072258 Add project to clients
up 20210724072900 Remove project from clients
up 20210724075541 Drop test camel
up 20210724080344 Rename students to people
up 20210724082720 Rename name to client name in clients
up 20210724083954 Change clients age to integer
↓ rails db:rollback 実行後
# rails db:migrate:status
database: rails_vue_development
Status Migration ID Migration Name
--------------------------------------------------
up 20210724031048 Create clients
up 20210724031617 Add name to client
up 20210724031825 Add name to client2
up 20210724033923 Add age to client
up 20210724070410 Create students
up 20210724070509 Create test camels
up 20210724072258 Add project to clients
up 20210724072900 Remove project from clients
up 20210724075541 Drop test camel
up 20210724080344 Rename students to people
up 20210724082720 Rename name to client name in clients
down 20210724083954 Change clients age to integer
schema_migrationsテーブルから削除されたバージョン「20210724083954」に該当するマイグレーションのStatusがdownになっています。
この状態でマイグレーションファイルを手動で削除すれば、このマイグレーションは無かったものにできます。
↓ db\migrate\20210725032240_create_users.rb を削除
# rails db:migrate:status
database: rails_vue_development
Status Migration ID Migration Name
--------------------------------------------------
up 20210724031048 Create clients
up 20210724031617 Add name to client
up 20210724031825 Add name to client2
up 20210724033923 Add age to client
up 20210724070410 Create students
up 20210724070509 Create test camels
up 20210724072258 Add project to clients
up 20210724072900 Remove project from clients
up 20210724075541 Drop test camel
up 20210724080344 Rename students to people
up 20210724082720 Rename name to client name in clients
なお、マイグレーションファイルを削除せずに、再度 rails db:migrate
を実行すれば、Statusがupに変わり、 schema_migrationsテーブルにそのバージョンが再び追加されます。
マイグレーションファイルを削除する方法
上記の流れでも少々触れましたが、実際にマイグレーションファイルを削除するときの注意点と、削除方法について実例で解説します。
マイグレーションを実行したあとに、間違いに気づいたり、修正したいと感じることがあると思います。
そのとき、基本的に、マイグレーションファイルを削除するのはNGです。マイグレーションファイルのみを削除すると、本来実行したはずの処理が見つからずロールバックでエラーが発生します。
マイグレーションファイルを削除したい場合は次のステップを踏む必要があります。
- 対象のファイルをマイグレーションしていない状態に戻す(DOWN)
- マイグレーションファイルの削除
- マイグレーションの実行(必要に応じて)
実例1|直近2つのマイグレーションを削除する
次のように直近2つのマイグレーションファイルを削除したい場合の例です。
- 最新のマイグレーション:
20210724083954 Change clients age to integer2
- 最後から2番目:
20210724083954 Change clients age to integer
# rails db:migrate:status
database: rails_vue_development
Status Migration ID Migration Name
--------------------------------------------------
up 20210724031048 Create clients
up 20210724031617 Add name to client
up 20210724031825 Add name to client2
up 20210724033923 Add age to client
up 20210724070410 Create students
up 20210724070509 Create test camels
up 20210724072258 Add project to clients
up 20210724072900 Remove project from clients
up 20210724075541 Drop test camel
up 20210724080344 Rename students to people
up 20210724082720 Rename name to client name in clients
up 20210724083954 Change clients age to integer
up 20210724085421 Change clients age to integer2
どちらもStatusがUPになっているので、マイグレーション済みです。この状態でファイルを削除してはいけません。
マイグレーションのバージョンを管理する、schema_migrationsテーブルを確認するとどちらも認識されています。(No.13まであります)
ロールバックする
まずはロールバックして、対象の2つのファイルをDOWNにします。
rails db:rollback STEP=2
# rails db:rollback STEP=2
== 20210724085421 ChangeClientsAgeToInteger2: reverting =======================
-- change_column(:clients, :age, :string)
-> 0.0447s
== 20210724085421 ChangeClientsAgeToInteger2: reverted (0.0449s) ==============
== 20210724083954 ChangeClientsAgeToInteger: reverting ========================
-- change_column(:clients, :age, :string)
-> 0.0036s
== 20210724083954 ChangeClientsAgeToInteger: reverted (0.0037s) ===============
ロールバック(reverting 引き戻しの意味)が2回実行されています。
この状態で、マイグレーションの状態を確認すると、削除したい最新の2つのStatusがdownになっていることがわかります。
# rails db:migrate:status
database: rails_vue_development
Status Migration ID Migration Name
--------------------------------------------------
up 20210724031048 Create clients
up 20210724031617 Add name to client
up 20210724031825 Add name to client2
up 20210724033923 Add age to client
up 20210724070410 Create students
up 20210724070509 Create test camels
up 20210724072258 Add project to clients
up 20210724072900 Remove project from clients
up 20210724075541 Drop test camel
up 20210724080344 Rename students to people
up 20210724082720 Rename name to client name in clients
down 20210724083954 Change clients age to integer
down 20210724085421 Change clients age to integer2
マイグレーションのバージョンを管理する、schema_migrationsテーブルを確認すると最新の2つのバージョンが削除され、No.11までになっています。
DBはマイグレーションのバージョンをNo.11の20210724082720までしか認識していない状態になりました。
これで、ようやく当初の2つのマイグレーションファイルを削除することができます。
ファイルを手動で削除して、マイグレーションの状態を確認すると先ほどのdownになっていた2つが削除できます。
# rails db:migrate:status
database: rails_vue_development
Status Migration ID Migration Name
--------------------------------------------------
up 20210724031048 Create clients
up 20210724031617 Add name to client
up 20210724031825 Add name to client2
up 20210724033923 Add age to client
up 20210724070410 Create students
up 20210724070509 Create test camels
up 20210724072258 Add project to clients
up 20210724072900 Remove project from clients
up 20210724075541 Drop test camel
up 20210724080344 Rename students to people
up 20210724082720 Rename name to client name in clients
実例2|指定したバージョンのみロールバックする
次のように既にマイグレーションが複数ある場合に、20210724075541 Drop test camel
というcamelテーブルを削除した処理のみロールバックします。
# rails db:migrate:status
database: rails_vue_development
Status Migration ID Migration Name
--------------------------------------------------
up 20210724031048 Create clients
up 20210724031617 Add name to client
up 20210724031825 Add name to client2
up 20210724033923 Add age to client
up 20210724070410 Create students
up 20210724070509 Create test camels
up 20210724072258 Add project to clients
up 20210724072900 Remove project from clients
up 20210724075541 Drop test camel
up 20210724080344 Rename students to people
up 20210724082720 Rename name to client name in clients
up 20210724083954 Change clients age to integer
どちらもStatusがUPになっているので、マイグレーション済みです。この状態でファイルを削除してはいけません。
マイグレーションのバージョンを管理する、schema_migrationsテーブルを確認するとどちらも認識されています。(No.11まであります。削除したいのはNo.9です)
ロールバックする
対象のバージョンを指定してロールバックします。
rails db:migrate:down VERSION=20210724075541
# rails db:migrate:down VERSION=20210724075541
== 20210724075541 DropTestCamel: reverting ====================================
-- create_table(:test_camels)
-> 0.2541s
== 20210724075541 DropTestCamel: reverted (0.2880s) ===========================
指定したバージョンでのみロールバックが実行されています。
この状態で、マイグレーションの状態を確認すると、対象のバージョンのみがdownになっていることがわかります。
# rails db:migrate:status
database: rails_vue_development
Status Migration ID Migration Name
--------------------------------------------------
up 20210724031048 Create clients
up 20210724031617 Add name to client
up 20210724031825 Add name to client2
up 20210724033923 Add age to client
up 20210724070410 Create students
up 20210724070509 Create test camels
up 20210724072258 Add project to clients
up 20210724072900 Remove project from clients
down 20210724075541 Drop test camel
up 20210724080344 Rename students to people
up 20210724082720 Rename name to client name in clients
up 20210724083954 Change clients age to integer
マイグレーションのバージョンを管理する、schema_migrationsテーブルを確認すると対象のバージョンが削除されています。
DBはマイグレーションのバージョン 20210724075541を認識していない状態になりました。
これで、ようやくマイグレーションファイルを削除することができます。
ファイルを手動で削除して、マイグレーションの状態を確認すると先ほどのdownになっていたバージョンが無くなります。
# rails db:migrate:status
database: rails_vue_development
Status Migration ID Migration Name
--------------------------------------------------
up 20210724031048 Create clients
up 20210724031617 Add name to client
up 20210724031825 Add name to client2
up 20210724033923 Add age to client
up 20210724070410 Create students
up 20210724070509 Create test camels
up 20210724072258 Add project to clients
up 20210724072900 Remove project from clients
up 20210724080344 Rename students to people
up 20210724082720 Rename name to client name in clients
up 20210724083954 Change clients age to integer
以上で処理は完了です。
schema_migrationsテーブルとマイグレーションファイルがズレる場合
ロールバックとマイグレーションの実行状況によっては、schema_migrationsテーブルとマイグレーションファイルを比較したときに並び順にズレが発生することがあります。
例えば、下記の例だと、 schema_migrationsテーブル の最新のバージョンは 「202107244075541」です。
ですが、マイグレーションファイルだと、最新は「20210724083954」で、 「202107244075541」 は4つ前にあります。
これは、rails db:migrate:down VERSION=20210724075541
で、 「202107244075541」 のみdownの状態にした後に、再度、rails db:migrate
を実行したため、
直近で実行したマイグレーションのバージョンが 「202107244075541」 となっている状態です。