ナガモト の blog

Full Cycle Developerを目指すエンジニアが有用そうな技術記事や、ポエムのようなよしなしごとを投稿するブログです。

Railsアプリのマスターデータ管理 Seed Fu ベタープラクティス

Railsアプリ開発におけるマスターデータの扱い方は大きく次の3つです。

  1. 標準機能のseedを利用する
  2. migrationを利用する
  3. その他

選択肢で言うとその他になりますが、私は複数の現場で導入してきたのSeed FuというGemをおすすめしています。この記事ではSeed Fuを利用する際のベタープラクティスを紹介します。

github.com

おすすめの記法

シンタックスシュガーとして用意されている下記の記法を用いましょう。マスターデータは基本的に静的な値を取り扱うもので、テーブルにレコードとして挿入されることがわかりやすいこの記法がおすすめです。Seed Fuを全く知らない人もすぐ理解してくれます。*1

都道府県のマスターデータを定義する例

Prefecture.seed(:id,
  { id: 1,  name: "北海道" },
  { id: 2,  name: "青森県" },
  { id: 3,  name: "岩手県" },
# ~中略~
  { id: 47, name: "沖縄県" }
)

開発環境用に大量のデータをループで挿入したいときなど、向かないと判断した時は他の記法を利用しましょう。ここでは説明しません。素直にREADMEを読みましょう。 github.com どうしても日本語情報がいい人は以下の記事を参照ください。 qiita.com

変更頻度に合わせた書き方

ほぼ確実に変更がありえず、誤って変更されることを防ぎたい場合はseed_onceで書きましょう。

Prefecture.seed_once(:id,
  { id: 1,  name: "北海道" },
  { id: 2,  name: "青森県" },
  { id: 3,  name: "岩手県" },
# ~中略~
  { id: 47, name: "沖縄県" }
)

こうするとid 1~47で存在しないものにのみinsertが行われるようになります。 (seedの場合は内容が異なるレコードは更新してくれます)

そもそもDBに保存するということは永続化したいけど変更もしたいマスターデータではないのか?という話もあります。プログラム内に定数で持つことも可能ですが、RDBに入れておくとBIツールでデータのみを参照する際にもわかりやすいといった利点もあります。 結局はケースバイケースです。その場に応じた判断をしましょう。

依存関係にあるマスターデータ挿入順序対策

マスターデータで外部キーを持つような依存関係になるものもあるでしょう。例えば都道府県と地方のような関係です。 Seed Fuはファイル名のアルファベット順で実行します*2*3。それを利用して、ファイルのプリフィックスに数字をつけましょう。一番最初に実行する必要があるファイルのプリフィックスは0です。私は同時実行で問題ないファイルも同じ数字を設定します。

地方のマスターデータを定義ファイル(00_areas.rb)

Area.seed_once(:id,
  { id: 1,  name: "北海道地方" },
  { id: 2,  name: "東北地方" },
# ~中略~
  { id: 8, name: "九州地方" }
)

都道府県のマスターデータ定義ファイル(01_prefectures.rb)

Prefecture.seed_once(:id,
  { id: 1,  name: "北海道", area_id: 1 },
  { id: 2,  name: "青森県", area_id: 2 },
  { id: 3,  name: "岩手県", area_id: 2 },
# ~中略~
  { id: 47, name: "沖縄県", area_id: 8 }
)

※二桁数字を採用しているのは開発用テストデータと合わせてファイル数が多くなることがあるためです。*4

同一ファイル内に書くことでより直感的に順序を制御する方法もあります。とはいえ、データが増えた時に見通しが悪くなったり、検索性が下がったりするため、私は1modelに対して1seedファイルを作成しています。

Seed Fu Gemを意識せずに実行させる

通常のrails db:seedコマンド実行時にSeed Fuが呼び出されるように設定しましょう。

db/seeds/seeds.rb

SeedFu.seed

標準的な方法ではrails db:seed_fuという簡単ですが独自コマンドが必要です。しかし、前述のように標準のseed実行時に呼び出す設定をすることで開発メンバー全員がGem特有のコマンドを覚えずにすみます。

また、この設定をすると標準のseedで管理していたものをSeed Fuに切り替えるときにも、CIやDeployの設定変更が不要になります。 (もともとseedを実行する設定になっているはずのため)

自動テスト実行前に一度だけマスターデータを挿入する

自動テストは副作用をなくす意味でもDBがクリーンな状態で実行されます。*5しかし、マスターデータについては存在することを前提に自動テストを実行したいはずです。 RSpecによる自動テストの場合、実行前に一度だけSeedFu.seedを実行するように次の記述をします。

spec/spec_helper.rb (rails_helper.rbの場合もある)

RSpec.configure do |config|
  config.before(:suite) do
    SeedFu.seed
  end
end

自動テストにおいて副作用をより厳密になくすため、Database Cleanerを合わせて利用する場合はこちらの記事がとても参考になります。 blog.inouetakuya.info

Seed Fuを用いたマスターデータ管理ベタープラクティスまとめ

  • シンタックスシュガーを利用したわかりやすい記法で書く
  • 変更頻度を意識し、seedseed_onceを使い分ける
  • ファイル名に実行順序を表すプリフィックスとして数字をつける
  • db/seeds/seeds.rbにSeedFu.seedと記述して標準のrails db:seedで実行可能にする
  • 自動テスト実行前にSeedFu.seedが実行されるように設定する

*1:当社比

*2:公式で明言されている仕様のため、gemのupdateによる挙動変更はなさそうです

*3:https://github.com/mbleigh/seed-fu/blob/master/README.md#where-to-put-seed-files

*4:開発用テストデータでは、1つのユーザの操作において複数のレコードが決まった順番で作成されることがある。そのときには10_xx.rb, 11_yy.rbとすることで10番台を1つの操作とみなしてまとめたりします

*5:テストで挿入されたデータはテスト終了後に削除する(される)のが一般的