【Rails入門】データベースを設定するrails db:migrateを説明!

Ruby on Rails(以降、rails)でWebアプリを開発していると、bin/rails db:migrateコマンドをよく使います。

しかし

・そもそもmigrationって?
・使い方がいまいち分からない…
・なんのためにあるの?
・ロールバック(rollback)とダウン(down)の違いが理解できない…

など、様々な疑問や不安を抱えながら使っている方もいらっしゃると思います!

そこで今回は、以下の内容について、実例を交えて解説します。

・migrationとは
・migrationのメリットとは
・bin/rails db:migrateコマンドの関連コマンドの使い方

それでは行ってみましょう!

目次

migrationとは

migrationbin/rails db:migrateコマンド)は、railsで使用するデータベースの構造(テーブル、カラム)を変更するときに利用する機能です。

migrationの大まかな流れは以下のとおりです。

(1)migrationファイルを作成します。

(2)bin/rails db:migrateコマンドを実行します。

この2つの手順を行うと、migrationファイルで指定したとおりにデータベースの構造が変更されます。

migrationのメリットとは

migrationを利用してデータベースの構造を変更すると、以下のようなメリットがあります。

・SQL文を書かずにデータベースの構造を変更できる
・migrationファイルを共有することで、複数の開発環境でデータベースの構造を簡単に共有できる
bin/rails db:migrate:statusコマンドで、migrationファイルの適用状況を確認できる
bin/rails db:rollbackコマンドで、データベース構造を少し前の状態に戻せる

migrationを理解するためにrailsの開発環境を作成する

migrationの動作を理解するために、railsの開発環境を構築しておきましょう。

railsの開発環境の構築方法は、以下の記事で解説していますので、ぜひご覧ください。

この記事では、app/samurai/migrate-demoディレクトリを作成して開発環境を構築した場合を例に、説明を続けます。

migrationファイルを作成する

migrationファイルの作成方法については、以下の記事で詳しく説明していますので、ぜひご覧ください!

この記事では、以下のコマンドを実行して5つのmigrationファイルを作成した場合を例に、説明を続けます。

bin/rails generate migration CreateSample name:string
bin/rails generate migration AddStringColToSample
bin/rails generate migration RenameFromStringColToAddressOnSample
bin/rails generate migration RemoveAddressFromSample
bin/rails generate migration AddIntegerColToSample

上記のコマンドで作成された5つのmigrationファイルは、以下のように編集してください。

db/migrate/20180704015653_create_sample.rb

sampleテーブルを作成します。

class CreateSample < ActiveRecord::Migration[5.1]
  def change
    create_table :samples do |t|
      t.string :name
    end
  end
end

※このファイルは変更しません。

db/migrate/20180704015654_add_string_col_to_sample.rb

string_colカラムを追加します。

class AddStringColToSample < ActiveRecord::Migration[5.1]
  def change
    add_column :samples, :string_col, :string
  end
end

db/migrate/20180704015655_rename_from_string_col_to_address_on_sample.rb

string_colカラムの名前を、addressカラムに変更します。

class RenameFromStringColToAddressOnSample < ActiveRecord::Migration[5.1]
  def change
    rename_column :samples, :string_col, :address
  end
end

db/migrate/20180704015656_remove_address_from_sample.rb

addressカラムを削除します。

class RemoveAddressFromSample < ActiveRecord::Migration[5.1]
  def down
    add_column :samples, :address, :string
  end
  def up
    remove_column :samples, :address
  end
end

db/migrate/20180704020017_add_integer_col_to_sample.rb

integer_colカラムを追加します。

class AddIntegerColToSample < ActiveRecord::Migration[5.1]
  def change
    add_column :samples, :integer_col, :integer
  end
end

migrationファイルを実行する(migrate)

作成したmigrationファイルを実行しますが、その前に、この時点でデータベース(テーブル)が作成されていないことを確認しましょう。

(1)以下のコマンドを1行ずつ順番に入力します。

bin/rails dbconsole
.tables

実行結果:
表示なし

次に、すべてのmigrationファイルをデータベースに反映しましょう。

(2)以下のコマンドを1行ずつ順番に入力します。

.exit
bin/rails db:migrate

実行結果:

== 20180704015653 CreateSample: migrating =====================================
-- create_table(:samples)
   -> 0.0041s
== 20180704015653 CreateSample: migrated (0.0042s) ============================

== 20180704015654 AddStringColToSample: migrating =============================
-- add_column(:samples, :string_col, :string)
   -> 0.0011s
== 20180704015654 AddStringColToSample: migrated (0.0012s) ====================

== 20180704015655 RenameFromStringColToAddressOnSample: migrating =============
-- rename_column(:samples, :string_col, :address)
   -> 0.0057s
== 20180704015655 RenameFromStringColToAddressOnSample: migrated (0.0059s) ====

== 20180704015656 RemoveAddressFromSample: migrating ==========================
-- remove_column(:samples, :address)
   -> 0.0054s
== 20180704015656 RemoveAddressFromSample: migrated (0.0057s) =================

== 20180704020017 AddIntegerColToSample: migrating ============================
-- add_column(:samples, :integer_col, :integer)
   -> 0.0009s
== 20180704020017 AddIntegerColToSample: migrated (0.0010s) ===================

すべてのmigrationファイルが実行されます。

この時点で、データベース(テーブル)が作成されていることと、テーブルのカラム情報を確認しましょう。

(3)以下のコマンドを1行ずつ順番に入力します。

bin/rails dbconsole
.tables

実行結果:

ar_internal_metadata  samples               schema_migrations

(4)以下のコマンドを入力します。

PRAGMA TABLE_INFO(samples);

実行結果:

0|id|integer|1||1
1|name|varchar|0|NULL|0
2|integer_col|integer|0||0

これがすべてのmigrationファイルを実行した場合のデータベースの構造です。

(5)以下のコマンドを入力します。

.exit

migrationファイルを途中まで反映する(VERSION)

上の手順では、すべてのmigrationファイルが反映されました。

ここでは、(すべてのmigrationファイルではなく)途中のmigrationファイルまでに限定して反映する方法を紹介します。

bin/rake db:migrateコマンドに、「VERSION=20180704015655」オプションを指定して実行します。

まずは、以下のように「VERSION=0」を指定して、いったん、すべてのmigrationファイルを未反映の状態に戻しましょう。

(1)以下のコマンドを入力します。

bin/rake db:migrate VERSION=0

実行結果:

Running via Spring preloader in process 5831
== 20180704020017 AddIntegerColToSample: reverting ============================
-- remove_column(:samples, :integer_col, :integer)
   -> 0.0052s
== 20180704020017 AddIntegerColToSample: reverted (0.0093s) ===================

== 20180704015656 RemoveAddressFromSample: reverting ==========================
-- add_column(:samples, :address, :string)
   -> 0.0003s
== 20180704015656 RemoveAddressFromSample: reverted (0.0004s) =================

== 20180704015655 RenameFromStringColToAddressOnSample: reverting =============
-- rename_column(:samples, :address, :string_col)
   -> 0.0072s
== 20180704015655 RenameFromStringColToAddressOnSample: reverted (0.0073s) ====

== 20180704015654 AddStringColToSample: reverting =============================
-- remove_column(:samples, :string_col, :string)
   -> 0.0084s
== 20180704015654 AddStringColToSample: reverted (0.0085s) ====================

== 20180704015653 CreateSample: reverting =====================================
-- drop_table(:samples)
   -> 0.0014s
== 20180704015653 CreateSample: reverted (0.0016s) ============================

次に、20180704015655(db/migrate/20180704015655_rename_from_string_col_to_address_on_sample.rb:string_colカラムの名前をaddressカラムに変更する)までのmigrationファイルを反映しましょう。

(2)以下のコマンドを入力します。

bin/rake db:migrate VERSION=20180704015655

実行結果:

Running via Spring preloader in process 5952
== 20180704015653 CreateSample: migrating =====================================
-- create_table(:samples)
   -> 0.0016s
== 20180704015653 CreateSample: migrated (0.0017s) ============================

== 20180704015654 AddStringColToSample: migrating =============================
-- add_column(:samples, :string_col, :string)
   -> 0.0004s
== 20180704015654 AddStringColToSample: migrated (0.0005s) ====================

== 20180704015655 RenameFromStringColToAddressOnSample: migrating =============
-- rename_column(:samples, :string_col, :address)
   -> 0.0058s
== 20180704015655 RenameFromStringColToAddressOnSample: migrated (0.0074s) ====

これで、20180704015655までのmigrationファイルが反映されました。

samplesテーブルにaddressカラムが作成されているはずです。

テーブルのカラム情報を確認しましょう。

(3)以下のコマンドを1行ずつ順番に入力します。

bin/rails dbconsole
PRAGMA TABLE_INFO(samples);

実行結果:

0|id|integer|1||1
1|name|varchar|0|NULL|0
2|address|varchar|0|NULL|0

確かにinteger_colカラムではなく、addressカラムがありますね。

(5)以下のコマンドを入力します。

.exit

環境を指定して実行する(RAILS_ENV)

railsでは、config/database.ymlでデータベース環境を設定できます。

ここまでに説明したbin/rails db:migrateコマンドを実行すると、development環境(db/development.sqlite3)に対してmigrationファイルが実行されます。

RAILS_ENVオプションを指定すると、test環境(db/test.sqlite3)やproduction環境(db/production.sqlite3)に対してmigrationファイルを実行できます。

test環境に対して実行する場合:

bin/rails db:migrate RAILS_ENV=test

production環境に対して実行する場合:

bin/rails db:migrate RAILS_ENV=production

migrationファイルの適用状態を確認する(status)

次に、migrationファイルの適用状態を確認する方法を紹介します。

ここまでの手順を順番に行っていれば、20180704015655までのmigrationファイルが実行されているハズです。

以下のコマンドを入力して、確認してみましょう。

bin/rake db:migrate:status

実行結果:

Running via Spring preloader in process 6564

database: /home/yazaki-mint/app/samurai/migrate-demo/db/development.sqlite3

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20180704015653  Create sample
   up     20180704015654  Add string col to sample
   up     20180704015655  Rename from string col to address on sample
  down    20180704015656  Remove address from sample
  down    20180704020017  Add integer col to sample

Status欄に「up」と表示されているmigrationファイルは、データベースに適用済みです。

一方、Status欄に「down」と表示されているmigrationファイルは適用されていません。

したがって、上の実行結果では、5つのmigrationファイルのうち3つ目(Migration ID:20180704015655)までが適用されていることがわかります。

特定のmigrationファイルを実行する(up/down)

他のmigrationファイルの実行結果に影響を受けない場合は、特定のmigrationファイルを実行しても問題ないでしょう。

今回の例では、3つ目までのmigrationファイルを適用済みのときに、5つ目(Migration ID:20180704020017:integer_colカラムを追加する)を適用したり、外したりしても問題ないでしょう。

一方、3つ目(Migration ID:20180704015655:string_colカラムの名前を、addressカラムに変更する)は、string_colカラムが無い状態で実行しても、実行されません。

3つ目のようなmigrationファイルは、他のファイルの実行結果に影響を受けますので、単独で実行することは避けましょう。

migrationファイルは順番に適用することを前提に作成されることが多いため、安易に一部だけを適用したり、逆に一部だけを外したりするべきではありません。

up/downは、影響範囲を十分に検討したうえで実行してください。

migrationファイルのupメソッドを実行する(up)

特定のmigrationファイルだけを実行するには、bin/rails db:migrate:upコマンドを実行します。

ここでは、5つ目(Migration ID:20180704020017:integer_colカラムを追加する)だけを適用してみましょう。

(1)以下のコマンドを実行します。

bin/rails db:migrate:up VERSION=20180704020017

実行結果:

== 20180704020017 AddIntegerColToSample: migrating ============================
-- add_column(:samples, :integer_col, :integer)
   -> 0.0010s
== 20180704020017 AddIntegerColToSample: migrated (0.0012s) ===================

migrationファイルの適用状況を確認しましょう。

(2)以下のコマンドを実行します。

bin/rake db:migrate:status

実行結果:

Running via Spring preloader in process 6726

database: /home/yazaki-mint/app/samurai/migrate-demo/db/development.sqlite3

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20180704015653  Create sample
   up     20180704015654  Add string col to sample
   up     20180704015655  Rename from string col to address on sample
  down    20180704015656  Remove address from sample
   up     20180704020017  Add integer col to sample

4つ目が未適用(down)のまま、5つ目(Migration ID:20180704020017:integer_colカラムを追加する)が適用(up)されていますね。

テーブルのカラム情報を確認しましょう。

(3)以下のコマンドを1行ずつ順番に入力します。

bin/rails dbconsole
PRAGMA TABLE_INFO(samples);

実行結果:

0|id|integer|1||1
1|name|varchar|0|NULL|0
2|address|varchar|0|NULL|0
3|integer_col|integer|0||0

確かにaddressカラムとinteger_colカラムの両方がありますね。

(4)以下のコマンドを入力します。

.exit

migrationファイルのdownメソッドを実行する(down)

今度は、5つ目(Migration ID:20180704020017:integer_colカラムを追加する)だけを外してみましょう。

特定のmigrationファイルだけを外すには、bin/rails db:migrate:downコマンドを実行します。

(1)以下のコマンドを実行します。

bin/rails db:migrate:down VERSION=20180704020017

実行結果:

== 20180704020017 AddIntegerColToSample: reverting ============================
-- remove_column(:samples, :integer_col, :integer)
   -> 0.0060s
== 20180704020017 AddIntegerColToSample: reverted (0.0078s) ===================

migrationファイルの適用状況を確認しましょう。

(2)以下のコマンドを実行します。

bin/rake db:migrate:status

実行結果:

Running via Spring preloader in process 6860

database: /home/yazaki-mint/app/samurai/migrate-demo/db/development.sqlite3

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20180704015653  Create sample
   up     20180704015654  Add string col to sample
   up     20180704015655  Rename from string col to address on sample
  down    20180704015656  Remove address from sample
  down    20180704020017  Add integer col to sample

5つ目(Migration ID:20180704020017:integer_colカラムを追加する)が外されました。

テーブルのカラム情報を確認してみましょう。

(3)以下のコマンドを1行ずつ順番に入力します。

bin/rails dbconsole
PRAGMA TABLE_INFO(samples);

実行結果:

0|id|integer|1||1
1|name|varchar|0|NULL|0
2|address|varchar|0|NULL|0

addressカラムはありますが、integer_colカラムが削除されています。

(4)以下のコマンドを入力します。

.exit

データベースを一度削除して初期化する(reset)

migrationファイルが増えてきたり、up/downで編集することが多くなったときは、再現性(冪等性:べきとうせい)を確認しましょう。

再現性を確認するためには、データベースを一度削除してから、すべてのmigrationファイルを適用して、実行結果を確認します。

データベースを一度削除したり再適用したりといった作業が大変そうなイメージですが、railsではbin/rails db:migrate:resetコマンドを実行するだけです。

(1)以下のコマンドを入力します。

bin/rails db:migrate:reset

実行結果:

Running via Spring preloader in process 6963
Dropped database 'db/development.sqlite3'
Dropped database 'db/test.sqlite3'
Created database 'db/development.sqlite3'
Created database 'db/test.sqlite3'
== 20180704015653 CreateSample: migrating =====================================
-- create_table(:samples)
   -> 0.0007s
== 20180704015653 CreateSample: migrated (0.0008s) ============================

== 20180704015654 AddStringColToSample: migrating =============================
-- add_column(:samples, :string_col, :string)
   -> 0.0007s
== 20180704015654 AddStringColToSample: migrated (0.0008s) ====================

== 20180704015655 RenameFromStringColToAddressOnSample: migrating =============
-- rename_column(:samples, :string_col, :address)
   -> 0.0161s
== 20180704015655 RenameFromStringColToAddressOnSample: migrated (0.0162s) ====

== 20180704015656 RemoveAddressFromSample: migrating ==========================
-- remove_column(:samples, :address)
   -> 0.0079s
== 20180704015656 RemoveAddressFromSample: migrated (0.0081s) =================

== 20180704020017 AddIntegerColToSample: migrating ============================
-- add_column(:samples, :integer_col, :integer)
   -> 0.0004s
== 20180704020017 AddIntegerColToSample: migrated (0.0004s) ===================

データベースの構造が、以下のようになっているでしょうか。

ぜひ確認してみてください。

0|id|integer|1||1
1|name|varchar|0|NULL|0
2|integer_col|integer|0||0

ロールバックする(rollback)

データベースの構造を前の状態に戻すことを、ロールバックすると言います。

migrationを利用していれば、bin/rails db:rollbackを使用するだけで、データベースの構造を一つ前の状態に戻せます。

migrationファイルを作成したときに、bin/rails db:migrateコマンドを実行する必要があったように、ロールバックするときも専用コマンド(bin/rails db:rollbackコマンド)を実行すると言うわけです。

なお、不要に思えるmigratoinファイルはロールバックに使用されますので、ロールバックが完了したら削除しましょう。

(1)以下のコマンドを実行します。

bin/rails db:rollback
== 20180704020017 AddIntegerColToSample: reverting ============================
-- remove_column(:samples, :integer_col, :integer)
   -> 0.0049s
== 20180704020017 AddIntegerColToSample: reverted (0.0069s) ===================

migrationファイルの適用状況を確認しましょう。

(2)以下のコマンドを実行します。

bin/rake db:migrate:status

実行結果:

Running via Spring preloader in process 7092

database: /home/yazaki-mint/app/samurai/migrate-demo/db/development.sqlite3

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20180704015653  Create sample
   up     20180704015654  Add string col to sample
   up     20180704015655  Rename from string col to address on sample
   up     20180704015656  Remove address from sample
  down    20180704020017  Add integer col to sample

まとめ

今回は、migration(bin/rails db:migrateコマンド)について解説しました。

migrationは、SQL文を書かずにデータベースの構造を変更したり、元に戻したりできる、非常に便利な機能であることがわかっていただけたでしょうか?

ぜひこの記事を参考に使ってみてください!

この記事を書いた人

侍エンジニア塾は「人生を変えるプログラミング学習」をコンセンプトに、過去多くのフリーランスエンジニアを輩出したプログラミングスクールです。侍テック編集部では技術系コンテンツを中心に有用な情報を発信していきます。

目次