LoginSignup
13
6

More than 3 years have passed since last update.

Rails の migration の仕組みを理解する

Last updated at Posted at 2019-07-07

普段 Rails で開発をしていて、migration の仕組みって、日付で migration ファイルが作成されて管理されてるってイメージで確認したことなかったので、動かしながら確認してみた。

とりあえず rails new してアプリを立ち上げるところまではやって DB を作るところからいじってみた。
DBは PostgreSQL を使っています。

初期状態

rails
➜  rails_migration git:(master) bin/rails db:create
Created database 'rails_migration_development'

この時点ではまだテーブルはない。

postgresql
rails_migration_development=# \d
Did not find any relations.

migration を実行して中身を確認

とりあえず model を作成

rails
➜  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

postgresql
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 ってカラムが一つだけある。

postgresql
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)

postgresql
rails_migration_development=# SELECT * FROM schema_migrations;
    version
----------------
 20190707095850
(1 row)

rollback してみる

これを rollback すると値も消える。

rails
➜  rails_migration git:(master) ✗ bin/rails db:rollback
== 20190707095850 CreateArticles: reverting ===================================
-- drop_table(:articles)
   -> 0.0245s
== 20190707095850 CreateArticles: reverted (0.0287s) ==========================
postgresql
rails_migration_development=# SELECT * FROM schema_migrations;
 version
---------
(0 rows)

もう一つテーブルを追加

テーブルを2つにして確認してみる

rails
➜  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)

postgresql
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 を実行する。

すると変更した日付の値が入る。

postgresql
rails_migration_development=# SELECT * FROM schema_migrations;
    version
----------------
 20190707095850
 20190706101406
(2 rows)

version にない日付の migration は実行されないと判断されて、実行される。

これを bin/rails db:rollback すると新しいものから消えていく

postgresql
rails_migration_development=# SELECT * FROM schema_migrations;
    version
----------------
 20190706101406
(1 row)

同じファイルで日付を変えてみる

bin/rails db:migrate でテーブルを戻しておいて、 user のファイルをコピーして
db/migrate/20190707101406_create_users.rb と一番新しい日付に修正後 migration を実行する。

rails
➜  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
➜  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
➜  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]
    # ~~~~~~~~~~ この部分

改めて手を動かして、テーブルのデータを確認しながら見ていくとちゃんと理解できた。

参考

13
6
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
13
6