【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】GitHubのセキュリティアラートで発見された脆弱性を解消する方法
# はじめに GitHubにはセキュリティアラートという機能があります。セキュリティアラートはリポジトリに含まれるライブラリやパッケージの脆弱性を定期的にチェックし、脆弱性のあるライブラリやパッケージが発見されたらアラートで知らせてくれるという機 [...]
2022年1月16日 10:36
【Rails】devise-two-factorを使った2段階認証の実装方法【初学者】
# はじめに Railsアプリで2段階認証を実装するには、「rotp」というGemを使う方法の他に、「devise-two-factor」というGemを使う方法があります。「devise-two-factor」はその名の通り、IDとパスワードによ [...]
2021年12月12日 17:58
【Rails】rotpを使った2段階認証の実装方法【初学者】
# はじめに 昨今はIDとパスワードによる認証だけでなく、ワンタイムパスワードによる2段階認証を導入するWebアプリが増えてきました。Railsで作成したWebアプリでも、IDとパスワードによる認証に加えて2段階認証を導入するニーズが高まっていま [...]
2021年11月27日 13:02
【Rails】deviseを使った認証機能の実装【初学者】
# はじめに Railsアプリに認証機能を導入するには「devise」というGemを使う方法が最も簡単です。「devise」は認証に係る機能をほとんどコードを書くことなく実装できる反面、処理がブラックボックス化されており、全容が把握しづらいという [...]
2021年11月27日 13:01
【Rails】IDとパスワードによる認証機能の実装【初学者】
# はじめに 大抵のWebアプリでは、ユーザー登録/解除、ログイン/ログアウトといった認証機能を持っています。Railsには簡単に認証機能を導入できる「device」というGemが用意されています。「devise」は多くのRailsアプリで使われ [...]
2021年11月27日 13:00