はじめに
Rails 6 に追加されそうな新機能を試す第56段。 今回は、 create_table if_not_exists
編です。
Rails 6 では、 create_table
に :if_not_exists
オプションが追加されました。
Ruby 2.6.3, Rails 6.0.0.rc1 で確認しました。Rails 6.0.0.rc1 は gem install rails --prerelease
でインストールできます。
$ rails --version
Rails 6.0.0.rc1
マイグレーションファイルを作る
$ bin/rails g migration CreateUser name
マイグレーションファイルを修正する
:if_not_exists
オプションを指定します。
class CreateUser < ActiveRecord::Migration[6.0]
def change
create_table :users, if_not_exists: true do |t|
t.string :name
end
end
end
データベースを作成する
データベースを作成します。
$ bin/rails db:create
psql でテーブルを作ります
先に users テーブルを psql で作成します。
ちょっと手抜きで id カラムは省略します。
app_development=# create table users ( name varchar(256) )
CREATE TABLE
テーブルができたことを確認しておきます。カラムの情報も確認します。
app_development=# \dt
List of relations
Schema | Name | Type | Owner
--------+----------------------+-------+----------
public | ar_internal_metadata | table | postgres
public | schema_migrations | table | postgres
public | users | table | postgres
(3 rows)
app_development=# \d users;
Table "public.users"
Column | Type | Collation | Nullable | Default
--------+------------------------+-----------+----------+---------
name | character varying(256) | | |
マイグレーションを実行する
マイグレーションを実行します。エラーは発生しません。
$ bin/rails db:migrate
== 20190713221959 CreateUser: migrating =======================================
-- create_table(:users, {:if_not_exists=>true})
-> 0.0019s
== 20190713221959 CreateUser: migrated (0.0019s) ==============================
psql で users テーブルを確認すると id カラムが無いので、マイグレーションによってテーブルはできなかったことがわかります。
app_development=# \d users;
Table "public.users"
Column | Type | Collation | Nullable | Default
--------+------------------------+-----------+----------+---------
name | character varying(256) | | |
ロールバックする
ロールバックしてみます。
$ bin/rails db:rollback
== 20190713221959 CreateUser: reverting =======================================
-- drop_table(:users, {:if_not_exists=>true})
-> 0.0031s
== 20190713221959 CreateUser: reverted (0.0043s) ==============================
psql で確認すると users テーブルが削除されていることがわかります。
app_development=# \dt
List of relations
Schema | Name | Type | Owner
--------+----------------------+-------+----------
public | ar_internal_metadata | table | postgres
public | schema_migrations | table | postgres
(2 rows)
マイグレーションを実行する
users テーブルが無い状態でマイグレーションしてみます。
$ bin/rails db:migrate
psql で確認するとテーブルが作られていることがわかります。
app_development=# \dt
List of relations
Schema | Name | Type | Owner
--------+----------------------+-------+----------
public | ar_internal_metadata | table | postgres
public | schema_migrations | table | postgres
public | users | table | postgres
(3 rows)
カラムの情報を確認すると id カラムも存在しており、 migration によって作られたことがわかります。
app_development=# \d users;
Table "public.users"
Column | Type | Collation | Nullable | Default
--------+-------------------+-----------+----------+-----------------------------------
id | bigint | | not null | nextval('users_id_seq'::regclass)
name | character varying | | |
Indexes:
"users_pkey" PRIMARY KEY, btree (id)
テーブルを削除してからロールバックする
psql で users テーブルを削除します。
app_development=# drop table users
ロールバックするとエラーが発生します。
$ bin/rails db:rollback
== 20190713221959 CreateUser: reverting =======================================
-- drop_table(:users, {:if_not_exists=>true})
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
PG::UndefinedTable: ERROR: table "users" does not exist
エラーを回避するには
エラーを回避するには、
class CreateUser < ActiveRecord::Migration[6.0]
def change
create_table :users, if_not_exists: true, if_exists: true do |t|
t.string :name
end
end
end
とするか
class CreateUser < ActiveRecord::Migration[6.0]
def up
create_table :users, if_not_exists: true do |t|
t.string :name
end
end
def down
drop_table :users, if_exists: true
end
end
とすれば良いようです。後者の方が直感的でわかりやすいので、個人的にはオススメです。
(というか前者は訳わからんし、将来、 ActiveRecord の動作が変わったりすると痛い目に合いそうです。)
試したソース
試したソースは以下にあります。
https://github.com/suketa/rails6_0_0rc1/tree/try056_create_table_if_not_exists