Rails4で 1対多、多対多関連しているときのフォームを作るなら、Nested Form Gemが便利

  • 137
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

※注意

この記事で紹介している nested form gem は2016/02/25の時点で3年間メンテナンスがされていないため、今でもメンテナンスされている類似gemの cocoon gem を利用した方が良さそうです。

参考記事:cocoon の nested_form との比較と導入方法 - Qiita

以下記事本文

多対多の関係にある時にフォームを作るとき、フォームでどんどん関連を追加していけるようにしたい場合には Nested Form Gemが便利というお話。

例えば、UserモデルとEventモデルが互いに多対多の関連で情報を持っているとする。
Event.jpg

ユーザーは参加申請フォームで一気に複数のEventに登録出来るようにしたいです。

まずは、gemから

#Gemfile
gem "nested_form"

を追加。

忘れずに app/assets/javascripts/application.jsに

//= require jquery_nested_form

を追加。

ひとまず、多対多の関係を作りたいので、中間モデル(Entry)を作る

#app/model/entry.rb
class Entry < ActiveRecord::Base
    belogns_to :event
    belogns_to :user
end

#app/model/event.rb
class Event < ActiveRecord::Base
    has_many :entries
    has_many :users, through: :entries
end

#app/model/user.rb
class User < ActiveRecord::Base
    has_many :entries
    has_many :events, through: :entries
    accepts_nested_attributes_for :entries, allow_destroy: true
end

Rails4系では attr_accessibleを書かずにコントローラーでStrong parameterで指定します。

#users_controller.rb
private
  def user_params
    params.require(:user).permit(entries_attributes: [:id, :event_id, :_destroy])

このように、中間テーブルのところをホワイトリストに追加してあげれば動くようになります。

そして、viewの方は

#app/views/users/_form.html.erb
<%= nested_form_for @user do |f| %>

<div class="field">
  <%= f.fields_for :entries do |entry| %>
    <%= render "entry_fields", f: entry %>
  <% end %>
  <p>
    <%= f.link_to_add "Add Event", :entries %>
  </p>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

#app/views/users/_entry_fields.html.erb
<li class="fields">
  <%= f.hidden_field :id %>
  <%= f.select :musician_id, options_for_select([['—', nil]] + Event.all.map{|m| [m.name, m.id]}, f.object.event_id) %>
  <%= f.link_to_remove 'Remove Event' %>
</li>

ってやると、

view.png

みたいな感じで、すべてのEventを取得し表示して選べるようにしてくれます。そして、Add Eventボタンを押せば複数のEventを指定できるようになります。