ゆうなんとかさんの雑記帳的な。

Twitterで踊ったり音ゲーしたりしてるあの名前がよくわからない人が書いてるらしいよ。

楽したいんですよ、綺麗にコードかけるし、書かなくていいかもしれないですし

昨日はもう出歩きたくないようなひどい雨風でしたが、今日は打って変わっていい天気です。涼しくなってきて夏も終わりが近いと感じます。
さて、今回は「多対多の関連を持つデータをRailsでうまく保存したく」なりました。

データを表示する段階の問題

newやeditで毎回

@hoges = Hoge.all
@piyos = Piyo.all

みたいなことをやっていました。これはこれでまあ悪くはないのですが、いちいちこれを書くのは面倒ですし、createやupdateでエラーが起きたらリダイレクト→入力したデータが消える\(^o^)/というユーザービリティが悪い状態でした。これはいけないなと思い、他の人が書いたコード参考に…

# この書き方をするには「multipluck」というgemが必要です。
def hoge_options
  Hoge.pluck [:name, :id]
end
def piyo_options
  Piyo.pluck [:name, :id]
end

というコードをヘルパーに書いて*1、ビューの方で

<%= f.select :hoge_id, options_for(hoge_options) %>
<% piyo_options.each |piyo| %>
  <%= f.check_box :piyo_id, piyo.last %><label><%= piyo.first %></label>
<% end %>

みたいに書けばすっきりするという結論に至りました。

保存するところでの問題

願わくば何も考えずに

User.create(params)

とかやりたいのですが、果たしてできるのでしょうか。調べてみると、accepts_nested_attributes_forを使って設定すればいいらしいことが分かりました。
モデル側に、

class User < ActiveRecord::Base
  attr_accessible :hoges, :user_hoges, :hoge_attributes
  has_many :user_hoges
  has_many :hoges, :through => :user_hoges

  accepts_nested_attributes_for :user_hoges
end

ビュー側に、

<%= f.fields_for :user_hoges |h| %>
  <%= h.hidden_field :user_id, @user.id %>
  <%= h.select :hoge_id, options_for(hoge_options), {}, :multiple => true %>
<% end %>

とか書いておけばOKです。多対多の場合、中間モデルをfields_forで指定するのがポイントです。

*1:multipluckを使わなくても[:name, :id].map{|s| Hoge.pluck s }.transposeでいけます