views:

160

answers:

1

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?

+1  A: 

Hello

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

  1. Fix the routes definition
  2. 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!!

fjuan