Rails 4 - Nested models(2 levels) not saving - ruby-on-rails

Noob question on nested models.
I am using Rails 4 and trying to create nested models as below:
Survey has many questions
Each Question has many answers
I am following Rails Casts episode #196 to create a survey, questions and answers in the same form. Surevey and Realted questions get saved but answers don't get saved to the database.(The answers fields however are being displayed right.)
I really appreciate your inputs on this.
Thanks,
Mike
surveys_controller.rb
def index
#surveys = Survey.all
end
def new
#survey = Survey.new
3.times do
question = #survey.questions.build
1.times { question.answers.build }
end
end
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,answer_attributes:[:content]])
end
new.html.erb
<h1>New survey</h1>
<%= render 'form' %>
<%= link_to 'Back', surveys_path %>
_form.html.erb:
<%= 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>
<!--Display Questions -->
<%= f.fields_for :questions do |builder| %>
<%= render 'question_fields', :f => builder%>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
_questions_fields.html.erb:
<p>
<%= f.label :content, "Question" %><br />
<%= f.text_area :content, :rows => 3 %>
</p>
<!--Display Answers -->
<%=f.fields_for :answers do |builder| %>
<p>
<%= render 'answer_fields', :f => builder%>
</p>
<%end%>
_answers_fields.html.erb:
<p>
<%= f.label :content, "Answer" %>
<%= f.text_field :content%>
</p>
Survey Model:
class Survey < ActiveRecord::Base
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions
end
Question model:
class Question < ActiveRecord::Base
belongs_to :survey
has_many :answers, :dependent => :destroy
accepts_nested_attributes_for :answers
end
Answer model:
class Answer < ActiveRecord::Base
belongs_to :question
end

Edit: Try changing answer_attributes to answers_attributes in the survey_params method.
Prying would have shown you that the answer attributes didn't get through.
After a quick look through I don't see anything particularly wrong, so you just need to debug. You should add gem 'pry-rails' to your Gemfile, then put
require 'pry'; binding.pry
on the line where you want to debug. I'd put it right before the save in your create action in the survey controller. Then submit the form and go to your server. It will be paused, and you will have a console. Type
#survey.save
#survey.errors
and see what comes up. Also type params and survey_params to make sure that your form data is all going through properly.
If you keep prying in different places you'll figure out what's not working. I suspect that your answers aren't being pulled into survey_params properly. Strong attributes is often the culprit when things aren't getting saved.

Related

Better way of handling complex form_for

(this is a re-post - I deleted the first post because I think I posted really poorly).
I'm new to programming, and I'm trying to handle an ROR form that sends data to 4 different models/tables, and at this point I'm banging my head into the wall. In sum, the use case is that a teacher inputs the following into a form: an error, a corresponding correction, an abstraction of the error, an abstraction of the correction, some tags that describe the abstraction, and an explanation.
When I press submit, I'm not getting any errors on the screen, but when I look at the server, the only thing that gets submitted successfully is the original error and correction - I get an unpermitted parameter: ec_abstractions for the rest (ec_abstractions is the first level of nesting). I'm starting to think that I'm just going about the entire question wrongly.
The form looks like this (maybe should be split into multiple forms on the same view page?)
<%= form_for #ec_explanation do |ec_explanation_form| %>
<% if #ec_explanation.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#ec_explanation.errors.count, "error") %> prohibited this error-correction pair from being saved:</h2>
<ul>
<% #ec_explanation.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%#= Insert here: all (or a single) q_response(s) that have not yet been added %>
<div class="field">
<%= ec_explanation_form.label :early_explanation %>
<%= ec_explanation_form.text_area :early_explanation %>
</div>
<div class="field">
<%= ec_explanation_form.label :full_explanation %>
<%= ec_explanation_form.text_area :full_explanation %>
</div>
<!--(this is just a test field for the "number_field" data type - I'm not convinced that we should input the stage like this)-->
<div class="field">
<%#= ec_explanation_form.label :stage %>
<%#= ec_explanation_form.number_field :stage %>
</div>
<%= ec_explanation_form.fields_for :ec_abstractions do |ec_abstractions_form| %>
<% if #ec_abstraction.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#ec_abstraction.errors.count, "error") %> prohibited this error-correction pair from being saved:</h2>
<ul>
<% #ec_abstraction.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= ec_abstractions_form.label :error_abstraction %>
<%= ec_abstractions_form.text_field :error_abstraction %>
</div>
<div class="field">
<%= ec_abstractions_form.label :correction_abstraction %>
<%= ec_abstractions_form.text_field :correction_abstraction %>
</div>
<%= ec_abstractions_form.fields_for :ec_pairs do |ec_pairs_form| %>
<div class="field">
<%= ec_pairs_form.label :error_phrase %>
<%= ec_pairs_form.text_field :error_phrase %>
</div>
<div class="field">
<%= ec_pairs_form.label :correction_phrase %>
<%= ec_pairs_form.text_field :correction_phrase %>
</div>
<% end %>
<%= ec_abstractions_form.fields_for :tags do |tags_form| %>
<div class="field">
<%= tags_form.label :tag, "Tag" %>
<%= tags_form.text_field :tag %>
</div>
<div class="field">
<%= tags_form.label :tag, "Tag" %>
<%= tags_form.text_field :tag %>
</div>
<div class="field">
<%= tags_form.label :tag, "Tag" %>
<%= tags_form.text_field :tag %>
</div>
<% end %>
<% end %>
(Include javascript (or bootstrap) that will generate extra tag fields onclick of a 'plus' button)
<div class="actions">
<%= ec_explanation_form.submit 'Submit Correction' %>
</div>
<% end %>
I have the following models:
class EcExplanation < ApplicationRecord
has_many :abstractions_explanations_joins
has_many :ec_abstractions, :through => :abstractions_explanations_joins
accepts_nested_attributes_for :abstractions_explanations_joins
end
class AbstractionsExplanationsJoin < ApplicationRecord
belongs_to :ec_explanation
belongs_to :ec_abstraction
accepts_nested_attributes_for :ec_abstraction
end
class EcAbstraction < ApplicationRecord
has_many :ec_pairs
has_many :tags_ec_abstractions_joins
has_many :tags, :through => :tags_ec_abstractions_joins
has_many :abstractions_explanations_joins
has_many :ec_explanations, :through => :abstractions_explanations_joins
accepts_nested_attributes_for :tags_ec_abstractions_joins
accepts_nested_attributes_for :ec_pairs
end
class EcPair < ApplicationRecord
belongs_to :response
belongs_to :ec_abstraction
end
class TagsEcAbstractionsJoin < ApplicationRecord
belongs_to :ec_abstraction
belongs_to :tag
accepts_nested_attributes_for :tag, :reject_if => lambda { |a| a[:tag].blank? }
end
class Tag < ApplicationRecord
has_many :tags_ec_abstractions_joins
has_many :ec_abstractions, :through => :tags_ec_abstractions_joins
end
And the following controller code:
class CorrectionStoragesController < ApplicationController
def index
#ec_explanations = EcExplanation.all
end
def show
end
def new
#ec_explanation = EcExplanation.new
#ec_abstraction = #ec_explanation.ec_abstractions.build
#ec_pair = #ec_abstraction.ec_pairs.build
3.times do
#tag = #ec_abstraction.tags.build
end
end
def edit
end
def create
#ec_explanation = EcExplanation.create(ec_explanation_params)
respond_to do |format|
if #ec_explanation.save
format.html { redirect_to new_correction_storage_path, notice: 'Correction storage was successfully created.' }
format.json { render :show, status: :created, location: #ec_explanation }
else
format.html { render :new }
format.json { render json: #ec_explanation.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #ec_explanation.update(ec_explanation_params)
format.html { redirect_to new_correction_storage_path, notice: 'Correction was successfully updated.' }
format.json { render :show, status: :ok, location: new_correction_storage_path }
else
format.html { render :edit }
format.json { render json: #ec_explanation.errors, status: :unprocessable_entity }
end
end
end
def destroy
#ec_explanation.destroy
respond_to do |format|
format.html { redirect_to correction_storages_url, notice: 'Correction was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def ec_explanation_params
params.require(:ec_explanation).permit(:early_explanation, :full_explanation, ec_abstractions_attributes: [:id, :error_abstraction, :correction_abstraction, ec_pairs_attributes: [:id, :error_phrase, :correction_phrase], tags_attributes: [:id, :tag]])
end
end
Any help would be greatly appreciated. Thanks
You will want to compare the parameters in log/development.log against what is permitted by CorrectionStoragesController#ec_explanation_params.
The error
unpermitted parameter: ec_abstractions
means that Strong Parameters rejected the parameter ec_abstractions because that is not in your whitelist. To have the form submit a parameter called ec_abstractions_attributes you need to set up accepts_nested_attributes_for in your EcExplanation model.
When your form has nested attributes like:
<%= form_for #ec_explanation do |ec_explanation_form| %>
...
<%= ec_explanation_form.fields_for :ec_abstractions do |ec_abstractions_form| %>
...
Then you need:
class EcExplanation < ApplicationRecord
accepts_nested_attributes_for :ec_abstractions

Rails 5: Object's parent ID being lost between the new action and the create action

I'm trying to create a post that will contain it's forum_id that the form resides in. I need both ID to save in the object to achieve that.
I initialize a new #post in the new action using #post = Forum.find(params[:forum_id]).posts.build
Which will spit out unsaved instance of a post containing it the forum_id just as intended.
I then fill out my form here:
<%= form_for #post, :url => {:controller => "posts", :action => "create"} do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %>
<%= f.text_field :title, class: "form-control" %>
</div>
<div class="field">
<%= f.label :description %>
<%= f.text_area :description, class: "form-control" %>
</div>
<div class="actions">
<%= f.submit class: "btn btn-primary" %>
</div>
<% end %>
And when I click the submit button and inspect post_params with byebug after the line #post = Post.new(post_params) in the create action, only the :title and :description come through. The forum_id is lost in between actions and I cannot the save #post without it. I have :forum_id whitelisted in my post_params but it's not coming through. I would think that if an instance of post is created in the new action with a forum_id that it should persist into the create action inside post_params but something is wrong here. Here is the relevant information that might help with my issue.
My model's relationships:
# User model
has_many :forums
has_many :posts
# Forum model
belongs_to :user
has_many :posts
# Post model
belongs_to :user
belongs_to :forum
# post_controller
def new
#post = Forum.find(params[:forum_id]).posts.build
end
Post Controller
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
...
# Rest of actions
...
def post_params
params.require(:post).permit(:title, :description, :forum_id, :user_id)
end
end
Form doesn't submit forum_id because it doesn't exist on there
I think you need to add this to that form
<%= f.hidden_field :forum_id %>

rails 4 nested_form not saving

I know there is a lot questions like this before, I have following all the answer, but still mine doesn't work. please help.
survey.rb
# app/models/survey.rb
class Survey < ActiveRecord::Base
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions, :reject_if => lambda { |a| a[:questions].blank? }, :allow_destroy => true
end
question.rb
# app/models/question.rb
class Question < ActiveRecord::Base
belongs_to :survey
end
surveys_controller.rb
# app/controllerss/surveys_controller.rb
def new
#survey = Survey.new
#survey.questions.build
end
def edit
end
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 :show, status: :created, location: #survey }
else
format.html { render :new }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
def survey_params
params.require(:survey).permit(:name, questions_attributes: [:id, :content, :_destroy])
end
_form.html.erb
# app/views/surveys/_form.html.erb
<%= nested_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 |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<%= f.fields_for :questions do |builder| %>
<div class="field">
<%= builder.label :content, "Question" %> <br>
<%= builder.text_field :content, :rows => 3 %>
<%= builder.link_to_remove "Remove this question" %>
</div>
<% end %>
<p><%= f.link_to_add "Add a question", :questions %></p>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Help? what do I miss?
:reject_if => lambda { |a| a[:questions].blank? }
a variable is a hash of attributes which will be passed to a question record. Your question model has no questions field, hence a[:questions] is always blank and the record it is rejected. Instead, do:
:reject_if => :all_blank

how do i get nested form value to input to database table, instead of nil?

I have a nested form in my rails app. The field that is nested is not adding values to the database.
I want an address added into my Places table. The address field is nested within a form that corresponds to the Post table.
I just pushed full code to github... http://goo.gl/wzjLK2
I think I am not doing something in my Posts Controller #CREATE
def create
#post = Post.new(post_params)
#place = Place.new params[:address]
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render action: 'show', status: :created, location: #post }
else
format.html { render action: 'new' }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
For reference, my Posts form:
<%= form_for(#post) do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :status %><br>
<%= f.text_field :status %>
</div>
<div class="field">
<%= f.label :upload %><br>
<%= f.text_field :upload %>
</div>
<%= f.fields_for #post.place do |p| %>
<div class="field">
<%= p.label :address %><br>
<%= p.text_field :address %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
My Posts model...
class Post < ActiveRecord::Base
belongs_to :place
belongs_to :user
has_many :comments
has_many :commenters, through: :comments, source: :user
accepts_nested_attributes_for :place
def place_for_form
collection = places.where(place_id: id)
collection.any? ? collection : places.build
end
end
Any help is so much appreciated. I've been stuck on this for two days now.
After running the code, I notice there is an error in the server console:
Unpermitted parameters: place
After change this #post.place in
<%= f.fields_for #post.place do |p| %>
<div class="field">
<%= p.label :address %><br>
<%= p.text_field :address %>
</div>
<% end %>
to :place, everything works fine.

Rails: has_many through association, and the form for creating new instances

I'm still super new with Rails, and just trying to get my first has_many through association set up.
Recipes have many ingredients, and each ingredient has an amount needed for the recipe. The ingredient_amount table has a recipe_id, an ingredient_id, and an amount.
When creating a new recipe, I want to be able to create these recipe/ingredient associations in the same place. In the end, I'm going to build an AJAX autocompleter for the ingredients. For now, as a baby step, I'd like to just assume the ingredient exists, and take care of checking once I've got this part down.
So, how can I make new.html.erb for recipes do this? How can I extend the form for more than one ingredient?
As it stands now, after going through http://weblog.rubyonrails.org/2009/1/26/nested-model-forms
I still can't get any fields to add ingredients. The current code is below.
class Recipe < ActiveRecord::Base
has_many :ingredient_amounts
has_many :ingredients, :through => :ingredient_amounts
accepts_nested_attributes_for :ingredient_amounts, :allow_destroy => true
end
class IngredientAmount < ActiveRecord::Base
belongs_to :ingredient
belongs_to :recipe
end
class Ingredient < ActiveRecord::Base
has_many :ingredient_amounts
has_many :recipes :through => :ingredient_amounts
end
Here's new.html.erb as I have it currently:
<h1>New recipe</h1>
<% form_for #recipe do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :instructions %><br />
<%= f.text_area :instructions %>
</p>
<p>
<%= f.label :numberOfServings %><br />
<%= f.text_field :numberOfServings %>
</p>
<p>
<%= f.label :prepTime %><br />
<%= f.text_field :prepTime %>
</p>
<p>
<% f.fields_for :ingredient_amounts do |ingredient_form| %>
<%= ingredient_form.label :ingredient_formedient_id, 'Ingredient' %>
<%= ingredient_form.collection_select :ingredient_id, Ingredient.all, :id, :name, :prompt => "Select an Ingredient"%>
<%= ingredient_form.text_field :amount %>
<% unless ingredient_form.object.new_record? %>
<%= ingredient_form.label :_delete, 'Remove:' %>
<%= ingredient_form.check_box :_delete %>
<% end %>
</p>
<% end %>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
<%= link_to 'Back', recipes_path %>
The important bits of the recipe controller:
def new
#recipe = Recipe.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #recipe }
end
end
def create
#recipe = Recipe.new(params[:recipe])
respond_to do |format|
if #recipe.save
flash[:notice] = 'Recipe was successfully created.'
format.html { redirect_to(#recipe) }
format.xml { render :xml => #recipe, :status => :created, :location => #recipe }
else
format.html { render :action => "new" }
format.xml { render :xml => #recipe.errors, :status => :unprocessable_entity }
end
end
end
And... I have no idea where to start in the ingredient_amounts controller.
This was my very first stab, and I'm pretty sure it's not so close :)
def new
#recipe = Recipe.find(params[:recipe_id])
#ingredient = Ingredient.find(params[:ingredient_id])
#ingredient_amount = Recipe.ingredient_amounts.build
end
Thanks for the help!
I believe what you are looking for is 'Nested Model Forms'.
Try this link: http://weblog.rubyonrails.org/2009/1/26/nested-model-forms
It's hard to know what to search for when you dont really know the terminology to begin with :)

Resources