Railsで、同じFormで同じModelに対しての修正と登録を混在させる(一度のrequestでinsertとupdateを同時に行う)
既存データの一覧を表示し、まとめて編集できる表において、
行を追加して新規データも登録できるようなケースを想定します。
単純にsubmitすると次のエラーが発生します。
expected Hash (got Array) for param `samples'
これはParameterに対し、既存データはidをキーとしたハッシュで格納され、
新規データはidがないのでハッシュの配列で格納されるためです。
既存データ例:
"comments"=>{
"1"=>{"id"=>"1", "title"=>"a", "content"=>"aa"},
"2"=>{"id"=>"2", "title"=>"b", "content"=>"bb"},
"3"=>{"id"=>"3", "title"=>"c", "content"=>"cc"},
"4"=>{"id"=>"4", "title"=>"d", "content"=>"dd"},
"5"=>{"id"=>"5", "title"=>"e", "content"=>"ee"},
"6"=>{"id"=>"6", "title"=>"f", "content"=>"ff"}
}
新規データ例:
"comments"=>[
{"id"=>"", "title"=>"a", "content"=>"aa"},
{"id"=>"", "title"=>"b", "content"=>"bb"},
{"id"=>"", "title"=>"c", "content"=>"cc"},
{"id"=>"", "title"=>"d", "content"=>"dd"},
{"id"=>"", "title"=>"e", "content"=>"ee"},
{"id"=>"", "title"=>"f", "content"=>"ff"}
]
回避策として、新規データにダミーidを割り振り
受け取ったときにidの有無を見て新規作成と更新を切り分けます。
View例:
<table>
<tr>
<th>title</th>
<th>content</th>
</tr>
<% (0..5).each do |i| %>
<% if @comments.present? && @comments[i].present? %>
<!-- 既存データ -->
<%= f.fields_for 'comments[]', @comments[i], index: @comments[i].id || "x#{i}" do |c| %>
<tr id="comment<%=i+1%>">
<%= c.hidden_field :id, :id => "id#{i+1}", :value => @comments[i].id %>
<td>
<%= c.text_field :title, :id => "title#{i+1}", :value => @comments[i].title %>
</td>
<td>
<%= c.text_field :content, :id => "content#{i+1}", :value => @comments[i].content %>
</td>
</tr>
<% end %>
<% else %>
<!-- 新規データ -->
<tr id="comment<%=i+1%>">
<input id="id<%=i+1%>" name="user[comments][x<%=i+1%>][id]" type="hidden"/>
<td>
<input id="title<%=i+1%>" name="user[comments][x<%=i+1%>][title]" />
</td>
<td>
<input id="content<%=i+1%>" name="user[comments][x<%=i+1%>][content]" />
</td>
</tr>
<% end %>
<% end %>
</table>
Parameters例:
(次の例では2つが既存データでidのない2つが新規データ)
"comments"=>{
"1"=>{"id"=>"1", "title"=>"a", "content"=>"aa"},
"2"=>{"id"=>"2", "title"=>"b", "content"=>"bb"},
"x3"=>{"id"=>"", "title"=>"c", "content"=>"cc"},
"x4"=>{"id"=>"", "title"=>"d", "content"=>"dd"},
}
Controller側で、idの中身を見て更新と新規登録を切り分けます。
--
動作確認用サンプルソースはGitHubにアップしています。