11

I cannot seem to get nested attributes to save to the database. I am using Rails 4.

Here are my models :

class Answer < ActiveRecord::Base
  belongs_to :question
end


class Question < ActiveRecord::Base
  has_many :answers
  belongs_to :survey
  accepts_nested_attributes_for :answers, allow_destroy: true
end

class Survey < ActiveRecord::Base
  has_many :questions
  validates_presence_of :name
  accepts_nested_attributes_for :questions
end

Here is the controller:

def create

  @survey = Survey.new(survey_params)

  respond_to do |format|
    if @survey.save
      format.html { redirect_to @survey, notice: 'Survey was successfully created.' }
      format.json { render action: 'show', status: :created, location: @survey }
    else
      format.html { render action: 'new' }
      format.json { render json: @survey.errors, status: :unprocessable_entity }
    end
  end
end

def survey_params
  params.require(:survey).permit(:name, questions_attributes:[:content, :id, :survey_id, 
    answers_attributes:[:content, :id, :questions_id]
    ])
end

Finally, here is the form itself. I have tried using both f.fields_for and just fields_for

<%= form_for(@survey) do |f| %>
  <% if @survey.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@survey.errors.count, "error") %> prohibited this survey from being saved:</h2>

      <ul>
      <% @survey.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>

  <%= f.fields_for :question do |builder| %>
     <%= builder.label :content %>
     <%= builder.text_area :content %>

       <%= f.fields_for :answer do |builder| %>
         <%= builder.label :content, "Answer" %>
         <%= builder.text_field :content %>
       <% end %>

  <% end %>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

The survey saves fine to the database, put the questions and answers will not. I've looked at every resource I could find and it seems like I am doing this correctly, so it's really driving me crazy.

This example is just a quick scaffold so I had some code to paste in, but I can't seem to get it to work anywhere else.

edit: changing my new action to something like this:

def new
  @survey = Survey.new
  @survey.questions.build
end

WORKED!

SEE TEEGS ANSWER FOR SOLUTION TO THIS

Here is the new form I am using, still only saving the survey name

  <%= f.fields_for :question do |question_builder| %>
  <%= form_for(@survey) do |f| %>
  <% if @survey.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@survey.errors.count, "error") %> prohibited this survey from being saved:</h2>

      <ul>
      <% @survey.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>

  <%= f.fields_for :question do |question_builder| %>
   <%= question_builder.label :content %>
   <%= question_builder.text_area :content %>

   <%= question_builder.fields_for :answer do |answer_builder| %> 
       <%= answer_builder.label :content, "Answer" %>
       <%= answer_builder.text_field :content %>
   <% end %>

<% end %>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>
NateW
  • 1,941
  • 3
  • 23
  • 37
Michael
  • 680
  • 3
  • 8
  • 18
  • Have you tried using `build` to create empty relations on your `new` action in the controller? – rlecaro2 Feb 11 '14 at 20:01
  • @rlecaro2 I tried changing my new action to this: ` def new @survey = Survey.new @survey.questions.build end` and it still did not work – Michael Feb 11 '14 at 20:13

3 Answers3

7

Looks like you're using the wrong form builder object. Change the question section to something like this:

<%= f.fields_for :question do |question_builder| %>
   <%= question_builder.label :content %>
   <%= question_builder.text_area :content %>

   <%= question_builder.fields_for :answer do |answer_builder| %> 
       <%= answer_builder.label :content, "Answer" %>
       <%= answer_builder.text_field :content %>
   <% end %>

<% end %>

Originally you were using the f form builder in the line f.fields_for :answers..., however given your model code, since it is a Question that accepts_nested_attributes_for an Answer, we simply exchange the form builder object for the one used for the question section.

Note: I changed the names of the builder objects for clarity.

Update

In order to build a deeply nested relationship like this, you will have to loop through your "built" association (questions in this case), and invoke build again on its has_many association. So going off of gabrielhilal's answer, you would do this:

def new
  @survey = Survey.new
  @survey.questions.build
  @survey.questions.each do |question|
      question.answers.build
  end
end

Note that in your case, since you are explicitly creating only one question, you can technically do this instead (instead of the loop):

@survey.questions.first.answers.build

Disclaimer: If there is a cleaner, more Rails-y, way to do this, I am sadly unaware of it.

Paul Richter
  • 10,414
  • 8
  • 47
  • 77
  • 1
    Woah, not sure how I missed that. However, after fixing it, I am still having the same issue. This is insanely puzzling. – Michael Feb 11 '14 at 20:34
  • @Michael Does gabrielhilal's answer help you at all? I forgot about the `.build` part. That may make a difference. – Paul Richter Feb 11 '14 at 20:37
  • yes it did! Fixing the form and adding the @survey.questions.build got the questions to save. I just need to figure out how to build the answers as well. I tried adding this to my model : `has_many :answers, through: :questions` and this to my controller `def new @survey = Survey.new @survey.questions.build @survey.answers.build end` however the answers field is not showing up. – Michael Feb 11 '14 at 20:48
  • @Michael Check out my update, let me know if that works. – Paul Richter Feb 11 '14 at 21:05
  • yes that worked! I don't know why I didn't think of that. Thank you so much! – Michael Feb 11 '14 at 21:07
4

In your controller you have questions_id:

def survey_params
  params.require(:survey).permit(:name, 
    questions_attributes: [
      :content, :id, :survey_id, 
      answers_attributes: [:content, :id, :questions_id]
    ]
  )
end

Is that a typo? Also try setting action_on_unpermitted_parameters to :raise in you development.rb to see if there is any attribute that you' re missing in your survey_params method.

# In case of unpermitted parameters in the controller raise error.
config.action_controller.action_on_unpermitted_parameters = :raise
  • It was a typo. I tried adding that setting to my development.rb and I am no errors are being raised when I save the survey. – Michael Feb 11 '14 at 20:34
  • Took me two an a half hours with my own similar problem before I saw this post. All I needed to do was put my `answers_attributes[]` inside my `questions_attributes[]`. You sir, are wonderful. – Darkstarone Apr 15 '14 at 00:55
3

You must change your new action to include @survey.questions.build:

def new
  @survey = Survey.new
  @survey.questions.build
end

But your form is wrong as well. Change the question to questions

 <%= f.fields_for :questions do |builder| %> #questions
   <%= builder.label :content %>
   <%= builder.text_area :content %>

You must do the same thing for answers.

gabrielhilal
  • 10,285
  • 6
  • 45
  • 74
  • This plus a combination of the stuff above I got the questions to save! Now how can get answers to build? I tried changing the relationship on survey to has_many :answers, through: questions and adding @survey.answers.build, but the form is not displaying for answers. – Michael Feb 11 '14 at 20:44