Rails 2.3.4で追加されたseeds.rbについて
Seed Dataをマイグレーションから隔離するための仕組みが追加された。
これの一番の意図としては
「マイグレーションの中にデータをいじるコードを含めるのやめようぜ。な!」
ってことのようだ。
参考: http://github.com/rails/rails/commit/4932f7b38f72104819022abca0c952ba6f9888cb
Seed Dataって?
まあなんとなくニュアンスは分かるけど、そもそもSeed Dataって何なんだという疑問。
上記を参考にすると
- ほとんど変更されないデータ
- 例: Userテーブルの管理者アカウントのような初期データ
- 例: アプリケーションの設定のためのデータ
ざっくり言うとアプリケーションの動作に必要な初期データ。
この中には都道府県マスタや権限マスタみたいなマスタも含めていいと思う。
db/seeds.rbの書き方
seeds.rbを開けば中に書いてあるサンプルコードですぐ分かるけど、単にseeds.rbにレコードを追加するコードを書いていくだけという単純な仕組みだ。
Category.create(:name => 'テント')
Category.create(:name => '寝袋')
とか
%w{テント 寝袋}.each do |name|
Category.create(:name => name)
end
とかひたすらRubyのコードを好きなように書くべしとなっている。
フォーマットをCSVとかXMLとかYAMLとかに限定するでもなく、新しいDSLを定義するでもなく、好きなようにコードで書けよという投げっぱなしぶりが潔い。
まあもともとマイグレーション内にまざってたデータ作成/編集コードを分離させたものに過ぎないわけだね。
rake db:seed
seeds.rbの適用はrake db:seedという新しいタスクで行う。
又はrake db:setupを実行した際に読み込まれる。
ただrake db:setupはデータベースをイチから作り直すという破壊と創造を司るタスクなので稼働中の本番環境でうっかり使ったりしないように注意。
rake db:seedの挙動はきわめてシンプル
rake db:seedを叩くとseeds.rbの内容がその都度実行される。
この時既存のデータベースの内容は加味してくれず、今までに何回rake db:seedが実行されたのかなどの考慮もない。
つまりマスタデータを追加するつもりで
%w{テント 寝袋}.each do |name|
Category.create(:name => name)
end
のようなコードを書いているとrake db:seedするたびにこれが何回も実行されてしまうので、ここらへんの面倒は自分で見ないといけないということだ。
例えば
Category.delete_all
%w{テント 寝袋}.each do |name|
Category.create(:name => name)
end
みたいな。
ただこの例の場合はIDが変わってしまうのでIDを固定にしたければもうちょっと凝ったコードにしないといけない。
しかしながら実際のところseeds.rbにはあんまり複雑なコードは書かない方がいいだろう。
複雑になりそうならモデルにメソッドを実装してseeds.rb内では単純にそれを呼び出すだけとかにした方が幸せになれるはず。
rake db:seedするタイミングについて
rake db:seedは最初に一回だけ実行するべきものなんじゃないか?
という説も考えられるがそれだと運用途中でマスタを追加した時とか使えないし、結局マイグレーションファイルの中にデータ追加コードを書きたくなってしまう。
ので多分それは違うだろう。
マスタの初期化や再構築のトリガーとしてはちょうどいいので、アプリケーションを修正したときなど運用中の任意のタイミングで使いたい。
ということでseeds.rbはrake db:seedがいつ実行されてもデータに破綻が起きないように書いておいた方が良さそうだ。
まとめ
こうして見ると「便利な仕組み追加しました」というより「マイグレーションの中でデータいじるな」って言うスタンスを明確にしたいというのが一番の目的なかんじ。
- データを作成/編集するコードはマイグレーションの中に書かない
- データを作成/編集するコードはdb/seeds.rbに好きなようにRubyのコードで書く
- rake db:seedで実行
seeds.rbのたぶんベターなプラクティス。
- 複雑な処理はモデルに実装する
- いつ実行されてもデータが破綻しないようにしておく
- 何度実行されてもデータが破綻しないようにしておく