【Rails】マイグレーションの書き方《実行篇》

はじめに

ActiveRecordの機能のひとつ、マイグレーションは便利な機能ですが、使い方を間違えるとせっかく作ったデータベースを破壊してしまいかねません。データベースの操作を間違えたときに有用なロールバックも、きちんと手順を守って行わないとマイグレーションの整合性が崩れてしまう可能性があります。

本記事では、マイグレーションの実行についてまとめています。

なお、マイグレーションの作成・編集については以下の記事でまとめていますので、併せてご覧いただければと思います。

マイグレーションの実行

ステータスコマンド

現在のマイグレーションがどこまで実行されているかなどをコマンドで確認することができます。特にチーム開発を行っているときやロールバックを行う前には必ず実行することをおすすめします。

$ rails db:migrate:status
database: db/development.sqlite3

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20190808140406  Create articles
   up     20190810152518  Add category to articles
   up     20190812090038  Add picture to articles

Statusがupならマイグレーションが実行されている、downならされていないという意味になります。Migration IDは、後述する特定のマイグレーションの実行などで必要となるバージョンです。

マイグレートコマンド

作成したマイグレーションを実行しデータベースの操作を行うコマンドです。最もよく使うコマンドだと思います。

$ rails db:migrate

特定のマイグレーションまで実行したい場合、マイグレーションのバージョンを指定することもできます。

$ rails db:migrate VERSION=20190812090038

このコマンドを実行すると、指定したバージョンを含むそれまでのマイグレーションがすべて実行されます。指定したバージョンが先の場合すべてのバージョンをupにし、前の場合すべてのバージョンをdownにします。

特定のマイグレーションだけをupまたはdownにすることもできます。

$ rails db:migrate:up VERSION=20190812090038
$ rails db:migrate:down VERSION=20190812090038

ロールバックコマンド

一度行ったマイグレーションを取り消したい場合、ロールバックコマンドを実行します。以下のコマンドは直近のマイグレーションだけを取り消します。

$ rails db:rollback

いくつかのマイグレーションを一気に取り消す場合、取り消すマイグレーション数を指定することもできます。

$ rails db:rollback STEP=5

このコマンドは直近から5世代前までのマイグレーションがすべて取り消されます。

トラブルシューティング

マイグレーションについてきちんと理解していないうちは、マイグレーションの実行周りでトラブルが起こりがちです。大抵は無理解からくる自分のミスが原因なわけですが、トラブルが起こったときは混乱して右往左往してしまいます。

マイグレーションを実行する際によく起こるであろうトラブルとそのベストプラクティスについてまとめてみました。

マイグレーションを修正したい場合

例えば、マイグレーションファイル作成時にカラム名を間違って記述してしまい、気付かないままマイグレーションを実行してしまった場合。最も簡単な方法は、ロールバックコマンドを実行してマイグレーションを取り消し、マイグレーションファイルを正しく修正してからもう一度マイグレーションを実行することです。

# マイグレートを取り消し
$ rails db:rollback

# マイグレーションファイルを修正
$ vi db/migrate/20190812090038_add_picture_to_articles.rb

# 再度、マイグレートを実行
$ rails db:migrate

では、間違ったマイグレーションが直近ではなくもっと前の世代だった場合はどうでしょうか。現在のマイグレーションと間違ったマイグレーションの間に正しいマイグレーションがいくつも存在している状態です。

この場合は、ステータスコマンドで間違ったマイグレーションまでの世代数を確認し、ロールバックコマンドでその世代までのマイグレーションをすべて取り消します。あとはマイグレーションファイルを正しく修正してからもう一度マイグレーションを実行します。

# ステータスを確認
$ rails db:migrate:status

# 世代を指定してマイグレートを取り消し
$ rails db:rollback STEP=5

# マイグレーションファイルを修正
$ vi db/migrate/20190812090038_add_picture_to_articles.rb

# 再度、マイグレートを実行
$ rails db:migrate

あるいは、間違いを修正するためのマイグレーションファイルを新たに作成するという方法もあります。カラム名の間違いくらいだったら、rename_columnを使って簡単に修正することができます。

# マイグレーションファイルを作成
$ rails generate migration RenameColumnFromArticles

# 作成したマイグレーションファイルを編集
$ vi db/migrate/20190814110732_rename_column_from_articles.rb

# マイグレーションを実行
$ rails:migrate

カラム名を修正するマイグレーションファイル。

class RenameColumnFromArticles < ActiveRecord::Migration
  def change
    rename_column :articles, :content, :body
  end
end

この方法ならロールバックする必要はありません。常に前進し続けるというスタイルです。デメリットとしては、ロールバックする場合と比べてマイグレーションの数が肥大化しがちだということです。その結果、デプロイ時などにマイグレーションが一気に実行されると時間がかかりすぎることに繋がります。

マイグレーションを削除したい場合

例えば、追加したカラムが不要になったので削除したい場合。通常であれば、カラムを削除するマイグレーションファイルを作成してマイグレーションを実行するという方法で十分でしょう。

# マイグレーションファイルを作成
$ rails generate migration RemoveCategoryFromArticles category:string

# マイグレートを実行
$ rails db:migrate

カラムを削除するマイグレーションファイル。

class RemoveCategoryFromArticles < ActiveRecord::Migration
  def change
    remove_column :articles, :category
  end
end

しかし、何らかの理由によりカラムを追加するマイグレーションごと削除してしまいたい場合はどうすればいいでしょうか。あまりないことではありますが、できないわけではありません。

まず、ステータスコマンドで削除したいマイグレーションのバージョンを確認します。そして、対象のマイグレーションだけを取り消し、最後にマイグレーションファイルを手動で削除します。

# ステータスを確認
$ rails db:migrate:status

# 対象のマイグレーションを取り消し
$ rails db:migrate:down VERSION=20190814110732

# マイグレーションファイルを削除
$ rm db/migrate/20190812090038_add_picture_to_articles.rb

取り消す前に削除してしまった場合

マイグレーションを取り消す前にマイグレーションファイルを削除してしまい、さらに復元することもできない場合。この状態でステータスを確認すると、以下のようになっているはずです。このような状態だと、マイグレーションファイルがないのでdownにすることもできません。

$ rails db:migrate:status
database: db/development.sqlite3

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20190808140406  Create articles
   up     20190810152518  Add category to articles
   up     20190812090038  ********** NO FILE **********

こうなってしまったら、一度ダミーのマイグレーションファイルを作成し、それからマイグレーションを取り消し、マイグレーションファイルを手動で削除します。

# ダミーのマイグレーションファイルを作成
# (重要:タイムスタンプは必ず一致させる)
$ touch db/migrate/20190812090038_dummy.rb

# 適当なマイグレーションを記述する
$ vi /db/migrate/20190812090038_dummy.rb

# マイグレーションを実行
$ rails db:migrate

# 対象のマイグレーションを取り消し
$ rails db:migrate:down VERSION=20190812090038

# マイグレーションファイルを削除
$ rm db/migrate/20190812090038_dummy.rb

ダミーのマイグレーションファイル。

class Dummy < ActiveRecord::Migration
  def change
  end
end

もう一度ステータスを確認するとNO FILEの表示が消えているはずです。

まとめ

トラブルシューティングのセクションでは、私が実際に陥ったトラブルについても書いています。当時はまだRuby on Railsを使い始めたばかりの頃だったので、ちんぷんかんぷんになってとにかく焦ったことを今でも覚えています。マイグレーションの実行周りはとにかくトラブルが起こりやすいので、慎重に行う必要がありますね。

本記事を参考にして、マイグレーションの実行について覚えていただければと思います。

関連記事

【Rails】Railsアプリのデバッグ《マルチデバイス篇》
# はじめに 近年のWebアプリはレスポンシブ対応が当たり前になっています。最低でもPCとスマートフォンに対応したデザイン、ときにはその中間のタブレットに対応したデザインなんかも作成する必要があります。 Webアプリの開発はPCを使って行う [...]
2021年5月23日 13:02
【Rails】Railsアプリのデバッグ《Better Errors篇》
# はじめに Railsアプリの開発中になんらかのエラーが発生すると、デフォルトでは以下のような画面が表示されます(画像をクリックすると拡大します)。 <a class="gallery" data-group="gallery" href [...]
2021年5月22日 19:42
【Rails】Railsアプリのデバッグ《byebug篇》
# はじめに Ruby on Railsに限りませんが、アプリの開発中にはエラーは付き物です。なにかしらのエラーが発生したときに、エラーの原因を特定しエラー箇所を修正することをデバッグと言います。Railsではデバッグの手助けとなる機能があらかじ [...]
2021年5月22日 15:30
【Rails】レンダリング(renderメソッド)でアンカー指定を行う
# はじめに 通常、Railsでアンカー付きのリクエストを発生させるには`redirect_to`を使います。 ```rb redirect_to root_path(anchor: 'target') ``` では、`rende [...]
2021年5月20日 10:47
【Rails】Bundler 2.2.x以降は開発者が適切なプラットフォームを追加する必要がある
# 事象 昔作ったRailsアプリを久しぶりに修正しデプロイしようとしたところ、以下のエラーが出力されました。 ```bash # 実行コマンド Running $HOME/.rbenv/bin/rbenv exec bundle ch [...]
2021年5月18日 16:18