普段 Rails で開発をしていて、migration の仕組みって、日付で migration ファイルが作成されて管理されてるってイメージで確認したことなかったので、動かしながら確認してみた。
とりあえず rails new
してアプリを立ち上げるところまではやって DB を作るところからいじってみた。
DBは PostgreSQL を使っています。
初期状態
➜ rails_migration git:(master) bin/rails db:create
Created database 'rails_migration_development'
この時点ではまだテーブルはない。
rails_migration_development=# \d
Did not find any relations.
migration を実行して中身を確認
とりあえず model を作成
➜ rails_migration git:(master) bin/rails g model Article title body
invoke active_record
create db/migrate/20190707095850_create_articles.rb
create app/models/article.rb
➜ rails_migration git:(master) ✗ bin/rails db:migrate
== 20190707095850 CreateArticles: migrating ===================================
-- create_table(:articles)
-> 0.0129s
== 20190707095850 CreateArticles: migrated (0.0131s) ==========================
すると 4 つテーブルができる。今回見ていくのは schema_migrations
。
rails_migration_development=# \d
List of relations
Schema | Name | Type | Owner
--------+----------------------+----------+--------
public | ar_internal_metadata | table | ikepon
public | articles | table | ikepon
public | articles_id_seq | sequence | ikepon
public | schema_migrations | table | ikepon
この schema_migrations
には version
ってカラムが一つだけある。
rails_migration_development=# \d schema_migrations
Table "public.schema_migrations"
Column | Type | Collation | Nullable | Default
---------+-------------------+-----------+----------+---------
version | character varying | | not null |
Indexes:
"schema_migrations_pkey" PRIMARY KEY, btree (version)
中の値を見ると、さっきの migration ファイルの日付の情報が入ってる。
(db/migrate/20190707095850_create_articles.rb)
rails_migration_development=# SELECT * FROM schema_migrations;
version
----------------
20190707095850
(1 row)
rollback してみる
これを rollback すると値も消える。
➜ rails_migration git:(master) ✗ bin/rails db:rollback
== 20190707095850 CreateArticles: reverting ===================================
-- drop_table(:articles)
-> 0.0245s
== 20190707095850 CreateArticles: reverted (0.0287s) ==========================
rails_migration_development=# SELECT * FROM schema_migrations;
version
---------
(0 rows)
もう一つテーブルを追加
テーブルを2つにして確認してみる
➜ rails_migration git:(master) ✗ bin/rails g model User name email
invoke active_record
create db/migrate/20190707101406_create_users.rb
create app/models/user.rb
➜ rails_migration git:(master) ✗ bin/rails db:migrate
== 20190707095850 CreateArticles: migrating ===================================
-- create_table(:articles)
-> 0.0129s
== 20190707095850 CreateArticles: migrated (0.0131s) ==========================
== 20190707101406 CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.0054s
== 20190707101406 CreateUsers: migrated (0.0056s) =============================
すると PostgreSQL には 2つの version
ができる。
(db/migrate/20190707095850_create_articles.rb)
(db/migrate/20190707101406_create_users.rb)
rails_migration_development=# SELECT * FROM schema_migrations;
version
----------------
20190707095850
20190707101406
(2 rows)
日付が前の migration を実行してみる
rollback して user のファイル名を
db/migrate/20190707101406_create_users.rb
から
db/migrate/20190706101406_create_users.rb
に修正して migration を実行する。
すると変更した日付の値が入る。
rails_migration_development=# SELECT * FROM schema_migrations;
version
----------------
20190707095850
20190706101406
(2 rows)
version
にない日付の migration は実行されないと判断されて、実行される。
これを bin/rails db:rollback
すると新しいものから消えていく
rails_migration_development=# SELECT * FROM schema_migrations;
version
----------------
20190706101406
(1 row)
同じファイルで日付を変えてみる
bin/rails db:migrate
でテーブルを戻しておいて、 user のファイルをコピーして
db/migrate/20190707101406_create_users.rb
と一番新しい日付に修正後 migration を実行する。
➜ rails_migration git:(master) ✗ bin/rails db:migrate
rails aborted!
ActiveRecord::DuplicateMigrationNameError:
Multiple migrations have the name CreateUsers.
migration の名前を変えてみる
Multiple migrations have the name CreateUsers.
同じ名前の migration があるって言われてるので、 migration の名前を class CreateUser < ActiveRecord::Migration[5.2]
(Users の s を削除)に変えて実行してみる。
するとエラーが変わった。
➜ rails_migration git:(master) ✗ bin/rails db:migrate
== 20190707101406 CreateUser: migrating =======================================
-- create_table(:users)
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
PG::DuplicateTable: ERROR: relation "users" already exists
さらに古い方(db/migrate/20190706101406_create_users.rb) を削除して再実行してみる。
➜ rails_migration git:(master) ✗ bin/rails db:migrate
== 20190707101406 CreateUser: migrating ======================================
-- create_table(:users)
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
PG::DuplicateTable: ERROR: relation "users" already exists
まとめ
- migration ファイルは日付で判断して、
schema_migrations
にないものを実行するようになっている。 - migration 内では同じ名前の class は使えない。
class CreateUser < ActiveRecord::Migration[5.2]
# ~~~~~~~~~~ この部分
改めて手を動かして、テーブルのデータを確認しながら見ていくとちゃんと理解できた。