はじめに
ActiveRecordでEnumカラムを実現しようとすると、一般的にはモデルにシンボル配列を定義して、カラム自体は数値型にする場合が多いようです。しかし、このやり方では以下の問題が発生します。
- 可読性が著しく落ちる
- ツールを利用してテーブルを閲覧しても、カラムの値が何を意味しているかわからない
- データベースを利用する為の情報が、アプリケーション(モデル)とデータベースに分離されてしまう
- データベースが、このActiveRecordを利用したアプリケーションに依存してしまう
- 特に、rubyを使わないアプリケーションからこのデータベースを利用する際に問題が生じる
一般に、データベースはアプリケーションより寿命が長く、また昨今では収集したデータをpythonで分析することも少なくない為、ActiveRecordにデータベースの構造を左右されたくありません。よって、意地でもEnum型のカラムを作りたいと思います。なお、データベースはMySQLを利用します。
環境
バージョン | |
---|---|
ruby | 2.5.1 |
ActiveRecord | 5.1.5 |
MySQL | 5.7.20 |
手順
といっても、やることはActiveRecord::ConnectionAdapters::TableDefinition#column(name, type)を利用して、typeに部分的なSQLを渡すだけ。
# samplesテーブルの作成
create_table 'samples', id: :integer, unsigned: true, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" do |t|
t.string 'name', limit: 100, default: '', null: false
# こんな感じでEnum型カラムを定義できる
t.column 'status', "ENUM ('none', 'failed', 'succeeded') DEFAULT 'none' NOT NULL"
t.datetime 'created_at'
t.datetime 'updated_at'
end
挿入/参照する場合、通常の文字列として利用できます。定義されていない文字列を挿入した場合、ActiveRecord::StatementInvalidが送出されます。
# モデルを定義
class Sample < ActiveRecord::Base
end
# 挿入
Sample.transaction do
model = Sample.create(name: 'Enoch', status: 'failed')
model.save! if model.valid?
end
# 参照
res = Sample.where(status: 'failed').last
puts res.name # `Enoch`