Editing multiple nested records simultaneously in Rails
I'm creating a simple quiz application where a question can have multiple answers. To improve the usability of my app, I want users to be able to edit ALL the answers for a given question in the same form:
I found this great Railscast/Asciicast episode which does almost EXACTLY what I want to do
http://asciicasts.com/episodes/198-edit-multiple-individually
However, there's a catch. My answers model is nested within questions like so:
map.resources 开发者_开发技巧:answers, :has_one => :question
map.resources :questions, :has_many => :answers
So when it comes time to define the route and form tag I come a bit unstuck... The tutorial suggests creating 2 new controller methods and defining the routes and form tags as follows:
map.resources :products, :collection => { :edit_individual => :post, :update_individual => :put }
<% form_tag edit_individual_products_path do %>
But this doesn't work in my case as Answers are dependent on Questions... Any ideas on how to translate this tutorial for my nested models?
Working with nested routes looks pretty from some point of view, but always become a bit tricky. In order for this to work you will have to
- Fix the routes definition
- Adapt the URL generators in all views (like form_for or answers_path)
Routes
First things first: Specifying associations within the routes won't allow you to add custom routes to the second class. I would do something like this:
map.resources :questions do |question|
question.resources :answers, :collection => {
:edit_individual => :post,
:update_individual => :put }
end
Views
It's really important to notice the change in URL generators:
- edit_answer_path(@answer) => edit_question_answer_path(@question, @answer)
- edit_individual_answer_path(@answer) => edit_individual_question_answer_path(@question, @answer)
I've made a fast adaptation of the Railscasts views:
<!-- views/answers/index.html.erb -->
<% form_tag edit_individual_question_answer_path(@question) do %>
<table>
<tr>
<th></th>
<th>Name</th>
<th>Value</th>
</tr>
<% for answer in @answers %>
<tr>
<td><%= check_box_tag "answer_id_ids[]", answer.id %></td>
<td><%=h answer.name %></td>
<td><%=h answer.value %></td>
<td><%= link_to "Edit", edit_question_answer_path(@question, answer) %></td>
<td><%= link_to "Destroy", question_answer_path(@question, answer), :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<p>
<%= select_tag :field, options_for_select([["All Fields", ""], ["Name", "name"], ["Value", "value"]]) %>
<%= submit_tag "Edit Checked" %>
</p>
<% end %>
<!-- views/answers/edit_individual.html.erb -->
<% form_tag update_individual_question_answers_path, :method => :put do %>
<% for answer in @answers %>
<% fields_for "answers[]", answer do |f| %>
<h2><%=h answer.name %></h2>
<%= render "fields", :f => f %>
<% end %>
<% end %>
<p><%= submit_tag "Submit" %></p>
<% end %>
Extra
As you may have seen, you will require the variable @question
within your views, so I would recommend you to have a before_filter
in your AnswersController that fetches the question object:
AnswersController
before_filer :get_question
[...]
private
def get_question
# @question will be required by all views
@question = Question.find(params[:question_id])
end
end
Enjoy your nested routes!!
精彩评论