はじめに
Rails(ActiveRecord)でenumを扱おうとすると、db上のデータはintegerとして扱うことが多いと思います。
しかしこれだとdb上のデータが何を意味しているかわかりづらく、可読性が下がります。
そこで今回はMySQLを使うことを前提に、MySQLのENUM型を定義する方法をメモしておこうと思います。
環境
ruby 3.0.0
rails 6.1.1
MySQL 5.7
手順
class CreateAnimals < ActiveRecord::Migration[6.1]
def change
create_table :animals do |t|
t.string :name
t.column :animal_type, "ENUM('dog', 'cat', 'bird')", null: false
t.timestamps
end
end
end
t.column :animal_type, "ENUM('dog', 'cat', 'bird')", null: false
この部分で定義しています。
t.column
でMySQLのENUM('dog', 'cat', 'bird')
という構文を直接実行できます。
途中からカラムを追加するには少し工夫が必要です。
change_table
で追加できますが、rollbackできないのでdef up
で定義しましょう。
class ChangeAnimals < ActiveRecord::Migration[6.1]
def up
change_table :animals do |t|
t.column :animal_type, "ENUM('dog', 'cat', 'bird')", null: false
end
end
def down
remove_column :animals, :animal_type
end
end
途中でENUMを拡張
class ChangeAnimals < ActiveRecord::Migration[6.1]
def up
change_table :attendance_punches do |t|
t.change :animal_type, "ENUM('dog', 'cat', 'bird', 'fish')", null: false
end
end
end
ENUMの型を増やすことはできますが、減らしたり、名称を変更しようとするとエラーになるので注意が必要です。
→すでに該当のENUMを持つデータが存在する場合に、そのENUMの種類を消すことはできないようです。(例:animal_type: dogを持つデータが存在した場合、dogを消すようなスキーマ変更はできない。)
enumerizeも使えます
class Animal
extend Enumerize
enumerize :animal_type, in: [:dog, :cat, :bird],
predicates: true,
scope: true
end
animal = Animal.new
animal.dog? # => false
animal.cat? # => false
animal.animal_type = 'dog'
animal.dog? # => true
animal.cat? # => false
Animal.with_animal_type(:bird)
# SELECT "animals".* FROM "animals" WHERE "animals"."animal_type" IN ('bird')
おわりに
カラム名をtype
にしたらエラーが出てmigrateできませんでした。どうやら予約語になっているらしく、type
をカラム名にすることはできないようです。