はじめに
Rails4.1からenumが追加され、便利に使えるようになりました。
参考: いまさらながらRails4.1から導入されたEnumが便利なのでまとめてみた
しかしenumは、想定外の値が挿入されることをデータベースのレイヤーで防ぐことはできず、SQLアンチパターン の第10章「31のフレーバー」のアンチパターンに当てはまります。
このような時は、enumに挿入される値をあらかじめ設定するマスターテーブルのようなものを作る必要があります。
そのサンプルを紹介します。
サンプルコードは、こちらのgithubにアップロードしています。
開発環境
- Rails:
5.0.0.1
- Database:
PostgreSQL 5.2
- 環境は
docker-compose
でセットアップします。
開発を始める際は、
git clone https://github.com/kawasin73/rails_enum_sample.git
cd rails_enum_sample.git
docker-compose up -d spring
docker-compose exec spring rails db:create
docker-compose exec spring rails db:migrate
docker-compose exec spring rails db:seed_fu
docker-compose up
主要なgem
seed_fu をマスターデータの挿入に利用します。
モデルの作成
今回は、id
, title
, mode
, category
の4つのカラムを持ったItem
モデルを作成します。
-
mode
は、integer
を持つenum -
category
は、string
を持つenum
にする予定です。
ItemMode モデル
mode
のマスターデータを保持するための、ItemMode
モデルを作成します。
class CreateItemModes < ActiveRecord::Migration[5.0]
def change
create_table :item_modes, id: :integer do |t|
t.string :name, null: false
end
end
end
id
は integer
型ですが、AUTO_INCREMENT を無効にするために、create_table :item_modes, id: :integer do |t|
と指定しています。
# == Schema Information
#
# Table name: item_modes
#
# id :integer not null, primary key
# name :string not null
#
class ItemMode < ApplicationRecord
validates :name, presence: true
end
ItemCategory モデル
category
のマスターデータを保持するための、ItemCategory
モデルを作成します。
class CreateItemCategories < ActiveRecord::Migration[5.0]
def change
create_table :item_categories, id: :string do |t|
end
end
end
# == Schema Information
#
# Table name: item_categories
#
# id :string not null, primary key
#
class ItemCategory < ApplicationRecord
end
Item モデル
最後にItemモデルを作成します。
class CreateItems < ActiveRecord::Migration[5.0]
def change
create_table :items do |t|
t.string :title, null: false
t.integer :mode, null: false, default: 0
t.string :category, null: false
t.timestamps
end
add_foreign_key :items, :item_modes, column: :mode, primary_key: :id
add_foreign_key :items, :item_categories, column: :category, primary_key: :id
end
end
ここで重要となるのが、add_foreign_key
外部キー制約の設定です。これによって、マスターデータに設定されている値以外をデータベースに挿入できなくします。
# == Schema Information
#
# Table name: items
#
# id :integer not null, primary key
# title :string not null
# mode :integer default(0), not null
# category :string not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Foreign Keys
#
# fk_rails_28ef78c62c (mode => item_modes.id)
# fk_rails_4fb37c41fb (category => item_categories.id)
#
class Item < ApplicationRecord
validates :title, presence: true
enum mode: { pending: 0, completed: 1, error: 2 }
enum category: { politics: 'Politics', economy: 'Economy', sports: 'Sports' }
end
enum を設定します。mode
はデフォルト値が0
のため、Item.new
した段階で自動的にpending
になるのも嬉しいところです。
また、ItemMode
と、ItemCategory
は、データベースの堅牢性のためだけに作成したモデルですので、 アプリケーションの中では利用しません。 そのため、belongs_to
や has_many
はあえて設定しません。
マスターデータの作成
マスターデータは、seed_fuで作成します。
Item.modes.each do |name, id|
ItemMode.seed do |item_mode|
item_mode.id = id
item_mode.name = name.to_s
end
end
Item.categories.values.each do |id|
ItemCategory.seed do |item_category|
item_category.id = id
end
end
この上で以下のコマンドを実行するとマスターデータが作成できます。
docker-compose up -d spring
docker-compose exec spring rails db:seed_fu
また、新しい値を設定する際は、Item
モデルのenumのハッシュを足して、rails db:seed_fu
するだけでマスターデータに追加することができます。
最後に
以上の設定で、enumを堅いデータベース設計の中で利用することができます。
ぜひ参考にしてください。
参考URL
- github - kawasin73/rails_enum_sample
- いまさらながらRails4.1から導入されたEnumが便利なのでまとめてみた
- SQLアンチパターン
- Rails だって硬いデータベース設計をしたい!そんなあなたに贈る Tips 4 選 - peroli Developer's Blog -> この記事は大変参考になった記事だったのですが、例の事件を受けてperoliさんのテックブログまで閉鎖されてしまったため現在は見ることができません。復活を祈ります。