はじめに
Rails 6 に追加された新機能を試す第113段。 今回は、MySQL enum set
編です。
Rails 6 では、MySQL の enum や set のカラムの schema dump が正しく出力されるようになりました。
Ruby 2.6.5, Rails 6.0.2.1, MySQL 8.0.16 で確認しました。 (Rails 6.0.0 でこの修正が入っています。)
$ rails --version
Rails 6.0.2.1
今回は、users テーブル (User モデル)に enum
と set
のカラムを追加して試してみます。
Rails プロジェクトを作成する
$ rails new rails_sandbox
$ cd rails_sandbox
User モデルを作成する
name
カラムだけもつ User
モデルを作成します。
enum
と set
のカラムは、後で、migration ファイルを直接編集して、追加します。
bin/rails g model User name
migration ファイルを編集する
migration ファイルを編集して、 enum
カラムの generation
と set
カラムの learning
を追加します。
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :name
t.column :generation, "enum('baby', 'toddler', 'preschool', 'gradeschool', 'teen', 'young_adult')"
t.column :learning, "set('piano', 'english', 'swimming', 'ballet', 'calligraphy')"
t.timestamps
end
end
end
seed データを作成する
機能を試すためには、作る必要は、無いのですが、一応、 seed データを1件作成します。
User.create(name: 'Dave', generation: 'gradeschool', learning: 'english,swimming')
マイグレーションを実行する
マイグレーションを実行します。
shell
$ bin/rails db:create db:migrate
スキーマファイルを確認する
できたスキーマファイルを確認します。
ActiveRecord::Schema.define(version: 2019_12_21_002717) do
create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "name"
t.column "generation", "enum('baby','toddler','preschool','gradeschool','teen','young_adult')"
t.column "learning", "set('piano','english','swimming','ballet','calligraphy')"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
end
Rails 5 では
Rails 5.2.4 では、スキーマファイルは以下のようになってしまいます。
generation
と learning
のカラムが string になってしまい、ENUM と SET であることがわからなくなってしまいます。
ActiveRecord::Schema.define(version: 2019_12_21_005503) do
create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "name"
t.string "generation", limit: 11
t.string "learning", limit: 41
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
MySQL で直接確認する
なお、MySQL で直接確認した場合は、Rails 6.0.2.1 も、Rails 5.2.4 も同じです。
mysql> show columns from users;
+------------+-----------------------------------------------------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-----------------------------------------------------------------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
| generation | enum('baby','toddler','preschool','gradeschool','teen','young_adult') | YES | | NULL | |
| learning | set('piano','english','swimming','ballet','calligraphy') | YES | | NULL | |
| created_at | datetime(6) | NO | | NULL | |
| updated_at | datetime(6) | NO | | NULL | |
+------------+-----------------------------------------------------------------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
bin/rails db:reset を試す
bin/rails db:reset
を実行してから、MySQL で直接確認すると
Rails 6.0.2.1 は、
mysql> show columns from users;
+------------+-----------------------------------------------------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-----------------------------------------------------------------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
| generation | enum('baby','toddler','preschool','gradeschool','teen','young_adult') | YES | | NULL | |
| learning | set('piano','english','swimming','ballet','calligraphy') | YES | | NULL | |
| created_at | datetime(6) | NO | | NULL | |
| updated_at | datetime(6) | NO | | NULL | |
+------------+-----------------------------------------------------------------------+------+-----+---------+----------------+
6 rows in set (0.01 sec)
と変わりないですが、Rails 5.2.4 では
mysql> show columns from users;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
| generation | varchar(11) | YES | | NULL | |
| learning | varchar(41) | YES | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
と変わってしまいました。
rails console からデータを検索する
(ここからはおまけです。)
bin/rails db:reset
で seed データが登録されたので、検索してみます。
irb(main):006:0> User.where(generation: 'gradeschool')
User Load (1.2ms) SELECT `users`.* FROM `users` WHERE `users`.`generation` = 'gradeschool' LIMIT 11
=> #<ActiveRecord::Relation [#<User id: 1, name: "Dave", generation: "gradeschool", learning: "english,swimming", created_at: "2019-12-21 02:04:41", updated_at: "2019-12-21 02:04:41">]>
irb(main):007:0> User.where("learning like '%swimming%'")
User Load (0.9ms) SELECT `users`.* FROM `users` WHERE (learning like '%swimming%') LIMIT 11
=> #<ActiveRecord::Relation [#<User id: 1, name: "Dave", generation: "gradeschool", learning: "english,swimming", created_at: "2019-12-21 02:04:41", updated_at: "2019-12-21 02:04:41">]>
正しくないデータを投入する
generation や、learning に不正なデータを指定して登録すると ActiveRecord::StatementInvalid
が発生します。
generation も learning も不正なデータの場合を試してみます。
最後のエラーメッセージが Data truncated for column 'generation' at row 1
であることから generation
に問題があることがわかります。
(が、どうして truncate されたか、ちょっとわかりにくいです...。)
irb(main):008:0> User.create(name: 'NG', generation: 'NG', learning: 'NG')
(0.5ms) BEGIN
User Create (1.6ms) INSERT INTO `users` (`name`, `generation`, `learning`, `created_at`, `updated_at`) VALUES ('NG', 'NG', 'NG', '2019-12-21 02:15:29.767037', '2019-12-21 02:15:29.767037')
(0.4ms) ROLLBACK
Traceback (most recent call last):
1: from (irb):8
ActiveRecord::StatementInvalid (Mysql2::Error: Data truncated for column 'generation' at row 1)
generation は正しいが、 learning が不正なデータの場合です。
最後のエラーメッセージが Data truncated for column 'learning' at row 1
であることから learning
に問題があることがわかります。
irb(main):009:0> User.create(name: 'NG', generation: 'gradeschool', learning: 'NG')
(0.4ms) BEGIN
User Create (1.2ms) INSERT INTO `users` (`name`, `generation`, `learning`, `created_at`, `updated_at`) VALUES ('NG', 'gradeschool', 'NG', '2019-12-21 02:15:50.398770', '2019-12-21 02:15:50.398770')
(0.2ms) ROLLBACK
Traceback (most recent call last):
2: from (irb):9
1: from (irb):9:in `rescue in irb_binding'
ActiveRecord::StatementInvalid (Mysql2::Error: Data truncated for column 'learning' at row 1)
Rails 5 では
bin/rails db:migrate
で作成した場合は、Rails 6 と同じ動作ですが、 bin/rails db:reset
すると Type がただの string になってしまうため、不正なデータでも登録できてしまいます。
ActiveRecord の enum との関係
ActiveRecord の enum
と MySQL の enum
は全く関係がなく、以下のように、 User モデルに enum
を定義しても、MySQL の generation カラムの enum とは連動せず、期待通りの動作はしません。
class User < ApplicationRecord
enum generation: %i[baby toddler preschool gradeschool teen young_adult]
end