LoginSignup
17
14

More than 5 years have passed since last update.

Railsでも堅いデータベース設計をする。堅いenumの使い方

Posted at

はじめに

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モデルを作成します。

db/migrate/20170120005654_create_item_modes.rb
class CreateItemModes < ActiveRecord::Migration[5.0]
  def change
    create_table :item_modes, id: :integer do |t|
      t.string :name, null: false
    end
  end
end

idinteger型ですが、AUTO_INCREMENT を無効にするために、create_table :item_modes, id: :integer do |t| と指定しています。

app/models/item_mode.rb
# == 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モデルを作成します。

db/migrate/20170120005937_create_item_categories.rb
class CreateItemCategories < ActiveRecord::Migration[5.0]
  def change
    create_table :item_categories, id: :string do |t|
    end
  end
end
app/models/item_category.rb
# == Schema Information
#
# Table name: item_categories
#
#  id :string           not null, primary key
#

class ItemCategory < ApplicationRecord
end

Item モデル

最後にItemモデルを作成します。

db/migrate/20170120010046_create_items.rb
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 外部キー制約の設定です。これによって、マスターデータに設定されている値以外をデータベースに挿入できなくします。

app/models/item.rb
# == 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_tohas_many はあえて設定しません。

マスターデータの作成

マスターデータは、seed_fuで作成します。

db/fixtures/001_item_mode.rb
Item.modes.each do |name, id|
  ItemMode.seed do |item_mode|
    item_mode.id = id
    item_mode.name = name.to_s
  end
end
db/fixtures/002_item_category.rb
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

17
14
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
17
14