Ruby on Rails(以降、rails)でWebアプリを開発していると、bin/rails db:migrateコマンドをよく使います。
しかし
・使い方がいまいち分からない…
・なんのためにあるの?
・ロールバック(rollback)とダウン(down)の違いが理解できない…
など、様々な疑問や不安を抱えながら使っている方もいらっしゃると思います!
そこで今回は、以下の内容について、実例を交えて解説します。
・migrationのメリットとは
・bin/rails db:migrateコマンドの関連コマンドの使い方
それでは行ってみましょう!
migrationとは
migration(bin/rails db:migrateコマンド)は、railsで使用するデータベースの構造(テーブル、カラム)を変更するときに利用する機能です。
migrationの大まかな流れは以下のとおりです。
(1)migrationファイルを作成します。
(2)bin/rails db:migrateコマンドを実行します。
この2つの手順を行うと、migrationファイルで指定したとおりにデータベースの構造が変更されます。
migrationのメリットとは
migrationを利用してデータベースの構造を変更すると、以下のようなメリットがあります。
・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文を書かずにデータベースの構造を変更したり、元に戻したりできる、非常に便利な機能であることがわかっていただけたでしょうか?
ぜひこの記事を参考に使ってみてください!