【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を使い始めたばかりの頃だったので、ちんぷんかんぷんになってとにかく焦ったことを今でも覚えています。マイグレーションの実行周りはとにかくトラブルが起こりやすいので、慎重に行う必要がありますね。

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

関連記事

【Ruby】Bundlerを使ってRubyGemsを作成/公開する方法
# はじめに Bundlerを使ってRubyGemsを作成および公開する方法について説明します。Bundlerを使わずにRubyGemsを作成/公開する方法については以下の記事を参照してください。 <iframe class="hatena [...]
2022年7月12日 23:18
【Ruby】RubyGemsを作成/公開する方法
# はじめに RubyGemsを作成および公開する方法について説明します。Bundlerを使ってRubyGemsを作成する方法については以下の記事を参照してください。 <iframe class="hatenablogcard" style [...]
2022年7月11日 21:52
【Rails】M1チップ搭載MacでRuby on Railsの開発環境構築
# はじめに M1チップ搭載MacにRuby on Railsの開発環境を構築する手順を記載します。 - MacBook Air (M1, 2020) - macOS Monterey 12.3.1 # Homebrew ## [...]
2022年5月5日 11:56
【Rails】Rakeタスクの基本情報と作成・実行方法
# はじめに Railsには標準でRakeというGemが同梱されています。RakeはRubyで実装されたMake(UNIX系のOSで使用できるコマンド)のようなビルド作業を自動化するツールです。Ruby Make、略してRakeというわけですね。 [...]
2022年3月7日 22:12
【Rails】モデルに外部キーを設定する方法とよく起こるエラー内容について
# はじめに Railsでモデルに外部キーを設定する方法について説明します。 # モデルに外部キーを設定する ## リレーションシップ 今回は1つのブログ記事は複数のコメントを持つ1対多のリレーションシップを例に説明します。現在は` [...]
2022年2月10日 14:18
【Rails】Capybaraのfill_inメソッドを実行すると「既存レコードの内容+指定した内容」がセットされる事象の原因と対処【RSpec】
# はじめに RSpec + Capybaraを使用して、Railsアプリの統合テストを実装しています。とあるモデルの編集画面において、入力フォームの内容を書き換えた上で送信し、レコードが更新されることを確認します。 入力フォームの内容を書 [...]
2022年1月27日 21:22
【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