LoginSignup
7
5

More than 3 years have passed since last update.

Rails6 のちょい足しな新機能を試す113(MySQL enum set 編)

Posted at

はじめに

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 モデル)に enumset のカラムを追加して試してみます。

Rails プロジェクトを作成する

$ rails new rails_sandbox
$ cd rails_sandbox

User モデルを作成する

name カラムだけもつ User モデルを作成します。

enumset のカラムは、後で、migration ファイルを直接編集して、追加します。

bin/rails g model User name

migration ファイルを編集する

migration ファイルを編集して、 enum カラムの generationset カラムの learning を追加します。

db/migrate/20191221002717_create_users.rb
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件作成します。

db/seeds.rb
User.create(name: 'Dave', generation: 'gradeschool', learning: 'english,swimming')

マイグレーションを実行する

マイグレーションを実行します。
shell
$ bin/rails db:create db:migrate

スキーマファイルを確認する

できたスキーマファイルを確認します。

db/schema.rb
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 では、スキーマファイルは以下のようになってしまいます。

generationlearning のカラムが string になってしまい、ENUM と SET であることがわからなくなってしまいます。

db/schema.rb
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 とは連動せず、期待通りの動作はしません。

app/models/user.rb
class User < ApplicationRecord
  enum generation: %i[baby toddler preschool gradeschool teen young_adult]
end

試したソース

参考情報

7
5
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
7
5