LoginSignup
14
18

【Ruby on Rails】マイグレーションの仕組みを理解して「分からない」から抜け出そう🚗【初心者向け】

Posted at

🌀 はじめに

記事を開いていただきありがとうございます!
この記事では主にRailsの初学者に向けて、マイグレーションの仕組みについて分かりやすく解説します!

私が初学の時にやっていた、 「とりあえず何か分からないけど rails db:migraterails db:rollback を実行してみる」 がみなさんから無くなるように、仕組みを理解できるように、順を追って説明していきます!

この記事で書く事
・ よく聞くマイグレーションとはそもそも何か
rails db:migraterails db:rollback って何してるの?

この記事で書かない事
・ SQLの解説
・ データベースの構造
・ Railsのコードの書き方

🛠 データベースの作り方

まずそもそもデータベース(RDB)はどうやって作るのでしょう?
データベースは MySQLPostgreSQL といったデータベースエンジンと呼ばれるものに、 「データベースを作るよ」「テーブルを追加するよ」 という命令を与えることで作ることができます。
このデータベースエンジンに与える命令を SQL といいます。
以下の操作は全て SQL を通して実現します。

  • データベースを作成する
  • データベースにテーブルを追加する
  • テーブルにカラムを追加する
  • カラムの型を変更する
  • カラムにインデックスを貼る

ここでは、「1つ1つ丁寧に作るんだー」ぐらいを把握してもらえればOKです!

Railsでは、この辺りをActiveRecordというライブラリが管理しているので、SQLについてあまり意識する必要はありません。Rails万歳!

📂 マイグレーション管理とは?

データベースを完成させるには、テーブルを作成したり、カラムを追加したりと、1つ1つのステップを踏む必要があると言いました。
このステップを踏んでデータベースを完成させることを マイグレーション といいます。

マイグレーション管理 とは、データベースの作成手順を小分けにして管理することです。
この作成手順を順に実行することで、データベースを完成させようという話ですね。

メリット

ではマイグレーション管理のメリットはなんでしょうか?
それは、 「データベースのバージョン管理ができる」 という話に尽きます。

例えば、みんな同じようなデータベースを持っていて、
自分がテーブルを追加したい!ってなった時、
「新しくテーブルを作成する」というステップだけ共有できたら 便利ではありませんか?

例えば、新しく「テーブルを作成する」というステップを用意して、
そのステップに間違いがあったことに気づいた場合、
もしそのステップだけやり直せたら ものすごく楽な話ではありませんか?

このように、データベースは完成させるまでに様々なSQLを実行しなければならないため、
それらをステップに区切って小分けにして管理するという方法があります。

Railsでは、マイグレーション管理を マイグレーションファイル というもので実現しています。
1つ1つのステップをマイグレーションファイル1つで表現します。

🏃‍♂️ マイグレーションについてもう少し

前セクションで もしそのステップだけやり直せたら と記述しましたが、マイグレーションにはやり直すという概念があります。
このステップをやり直す操作を 「ロールバック」 とか言ったりします。

例えば、「テーブルを作成する」のやり直しは 「テーブルを削除する」 、「カラムを追加する」のやり直しは 「カラムを削除する」 といった具合です。

ステップごとにロールバックができるということもマイグレーション管理のメリットになるので覚えておきましょう。

✍️ Railsでのマイグレーションファイルの書き方は?

Railsでは、ステップをマイグレーションファイルに書きます。
例えば、 users テーブルを作成するステップのマイグレーションファイルは以下のように記述します。

class CreateUsers < ActiveRecord::Migration[5.0]
  # ステップを change に記述する
  def change
    create_table :users do |t|
      t.string :name
      t.timestamps
    end
  end
end

create_table という命令で、 nametimestamps を持った users テーブルを作成するんですね。

create_table(テーブルを作成する) の他にもいろいろあるので、ぜひ調べてみてください。
参考: Railsガイド マイグレーション

マイグレーションを実行する

そして、ステップを進める マイグレーション を実行する時は rails db:migrate というコマンドを実行します。
上の記述した後 rails db:migrate を実行すると、実際に users テーブルが作成されます。

この状態で、もう一度 rails db:migrate を実行してみるとどうなるでしょうか?
実は何も起きません!!
これが前述したマイグレーション管理のメリットで、ステップに区切ったので実行済みのステップはもう実行しなくていいんですね。すごい!

ロールバックを実行する

また、Railsでマイグレーションができたので当然ロールバックもできます。
マイグレーションを実行した上で rails db:rollback というコマンド実行すると、 users テーブルが削除されます。
このとき、 「テーブルを作成する」 のやり直しは 「テーブルを削除する」Railsが自動で判断してくれている ので、やり直し手順を記述する必要がないんです!

もしやり直し手順も明示したければ、以下のように記述することもできます!

class CreateUsers < ActiveRecord::Migration[5.0]
  # ステップを進める手順を up に記述する
  def up
    create_table :users do |t|
      t.string :name
      t.timestamps
    end
  end

  # ステップをやり直す手順を down に記述する
  def down
    drop_table :users
  end
end

🔍 Railsはどうやってマイグレーション管理している?

前セクションで、 もう一度マイグレーションを実行しても何も起きないと言いました。
つまりそれは、 どこまでマイグレーションを実行したかをRailsが把握している ということです。
Railsはどのように把握しているのでしょうか?

実は、 Railsがデータベースに schema_migrations というテーブルを勝手に用意していて、マイグレーション実行履歴をそこで勝手に管理している んです!
ActiveRecord::SchemaMigration.all とか実行すると、その中身が見れます。

マイグレーションファイルのバージョン

Railsのマイグレーションファイルには、 バージョン という概念が存在し、それはファイル名に書かれています。
マイグレーションファイルを作成すると、以下のようなファイル名になると思います。

20230717043513_create_users.rb

この 20230717043513 という日付がこのマイグレーションファイルのバージョンを表しています。
データベースを構築するステップには順番があるため、それをマイグレーションファイルを作成した時系列で管理しているんですね。すごいぞRails。

schema_migrationsの中身

schema_migrations は、実行したマイグレーションファイルのバージョンを保持できるようになっており、 schema_migrations テーブルにバージョンがあれば、そのマイグレーションファイルは実行済みとなるわけです。

rails db:migrate を実行すると、 schema_migrations テーブルにバージョンがないマイグレーションファイルを古い順に実行していき、今回実行したバージョンを追加で schema_migrations テーブルに格納します。
また rails db:rollback を実行すると、やり直し実行後に schema_migrations からバージョンを削除します。

このようにして、Railsはマイグレーション管理を実現しています。
我々から見えない裏側で本当に色々な仕組みが動いているんですね!

🦶 よくある躓きポイント

全体の解説は以上になりますが、ここからは僕が初学の頃によく引っかかっていたRailsのマイグレーションの罠を紹介します。
もし皆さんが躓いているところのヒントになれば幸いです。

それは 「マイグレーション実行時にエラーが出た時」 に起こります。
例えば、以下のマイグレーションファイルがあるとします。

class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :name
      t.timestamps
    end
    add_index :users, :email
  end
end

これは、「 users テーブルを作成して、 users テーブルの email カラムにインデックスを貼る」というステップのマイグレーションファイルです。

これでマイグレーションを実行するとどうなるでしょうか?
インデックスを貼る時に、 users テーブルに email カラムが無い!」 と怒られますね。
やっちゃったー、実は email カラムではなく name カラムにインデックスを貼りたかったんです。

それでは、 add_index を以下のように修正して、

class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :name
      t.timestamps
    end
    add_index :users, :name # :email を :name に修正
  end
end

再度マイグレーションを実行します。
そうすると今度は、 users テーブルは既にある!」 と怒られます。
ええぇ、何が起こったのでしょうか、、?

1回目の実行時

このマイグレーションファイルのステップは、 「テーブルを作成する」 と、 「インデックスを貼る」 という2つの操作から成ります。
よって1回目に実行した時、以下のように事が進みました。

  1. マイグレーションを実行しようとする
  2. users テーブルを作成しようとする。
  3. 無事に users テーブルが作成される。
  4. users テーブルの email カラムにインデックスを貼ろうとする。
  5. users テーブルに email カラムが無い!」 と怒られる。

なるほど。ここまでは想像しやすいですね。
では修正後の2回目はどうでしょうか?

2回目の実行時

2回目は以下のように事が進みます。

  1. マイグレーションを実行しようとする
  2. users テーブルを作成しようとする。
  3. users テーブルは既にある!」 と怒られる。

確かに!1回目で users テーブルが作成されて、2回目にも作成しようとしちゃってる!
という事ですね。

Railsが実行済みを把握できるステップというのは、あくまで マイグレーションファイル単位 なので、 途中で失敗してもどこまで実行済みかは把握できない んです。

ではどうしたら良いか

これは簡単です。実行済みのところはコメントアウトしてしまえばいいんです。

class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    # create_table :users do |t|
    #   t.string :name
    #   t.timestamps
    # end
    add_index :users, :name # :email を :name に修正
  end
end

この状態で再度マイグレーションを実行すると、インデックスを貼ることに成功して、無事にマイグレーションは終了します。
そして、このマイグレーションファイルはマイグレーションに成功したので、 schema_migrations にバージョンが記録され、ロールバックするまでは実行されないように成ります。

最後に、 create_table のコメントアウトを外せば、 「テーブルを作成する」 と 「インデックスを貼る」 の操作が完了して万々歳です!

成功後にコメントアウト外すのを忘れずに!

この記事の話を理解できていれば、何が起きていたのか見えてくるのではないでしょうか。

🌀 さいごに

ここまで読んでいただきありがとうございます!
少しでも分かりやすくなれと、だいぶ噛み砕いて説明したので、不明点などあればコメントでお願いします!
少しでも初学者さんの力になれば幸いです!

14
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
18