idと名前を関連付けるformのselectはcollection_selectメソッドを使おう
collection_selectメソッドの存在を見落としていてselectメソッドで名前を取得して、controllerでそれを対応するidに変換してModel#save
してしまっていた。
そんなときはcollection_select
メソッドがめちゃ便利なので使い方メモっておく。
定義部分はactionview/lib/action_view/helpers/form_options_helper.rb
にあり、以下のようになってる。
# Wraps ActionView::Helpers::FormOptionsHelper#collection_select for form builders: # # <%= form_for @post do |f| %> # <%= f.collection_select :person_id, Author.all, :id, :name_with_initial, prompt: true %> # <%= f.submit %> # <% end %> # # Please refer to the documentation of the base helper for details. def collection_select(method, collection, value_method, text_method, options = {}, html_options = {}) @template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options)) end
methodっていうのがselectタグのname属性になる。
collectionってのは対応するモデルのデータ.all
でokだけど、viewでActiveRecordのメソッド使うのっておかしい感じするのでcontrollerかmodelで必要なものを取り出してインスタンス変数で渡すのがよい気がしたんだけど、それだとvalidationで例外が発生した時にsaveが完了しなかったときの処理(同じページのレンダリングとか)までたどり着いてくれない...いい方法あれば知りたい。
そしてvalue_methodが実際にhtmlタグ内でvalueのとこに格納される値、text_methodがoptionタグの中に挿入される値って感じになる。
まとめると、
<%= f.collection_select :person_id, Author.all, :id, :name_with_initial, prompt: true %>
は、下のように変換される。
<select name="person_id"> <option value="id">name_with_initial</option> ... </select>
便利ですなー