(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
Related
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
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.
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.
In my post view, I have a _form with a title and releaseDate parameter.
In my controller I get the value from params[:post][:title], but the params[:post][:releaseDate] is empty.
post _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 :title %><br />
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :releaseDate %><br />
<%= f.datetime_select :releaseDate %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
posts_controller
def create
#title = params[:post][:title]
#date = params[:post][:releaseDate]
#post = Post.new(title: #title, releaseDate: #date)
#post.user_id = current_user.id
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render json: #post, status: :created, location: #post }
else
format.html { render action: "new" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
(I am not using #post = Post.new(params[:post]) because I want to do something with the title and releaseDate in the controller.)
post.rb (model)
class Post < ActiveRecord::Base
attr_accessible :releaseDate, :title, :user_id
belongs_to :user
end
Any suggestions on how I can get the datetime value from the form and into my controller?
releaseDate is not rails convention, try to rename field to release_date. Maybe this will help.
To proof this concept you can use so called virtual attributes: add release_date getter and setter to model like this (note that logs can help you to narrow down the problem) :
class Post < ActiveRecord::Base
attr_accessible :releaseDate, :title, :user_id
belongs_to :user
def release_date
date = read_attribute('releaseDate')
logger.debug("READ DATE IS #{date} aND CLASS IS #{date.class}") # see this in rails server logs
end
def release_date=(date)
logger.debug("WRITE DATE IS #{date} aND CLASS IS #{date.class}") # see this in rails server logs
write_attribute('releaseDate', date)
end
end
And in view replace all releaseDate with release_date
I've defined nested resources in the /config/routes.rb
resources :goals do
resources :goal_entries
end
Models for Goal:
class Goal < ActiveRecord::Base
attr_accessible :code, :description, :from_date, :to_date
validates_uniqueness_of :code
validates_presence_of :code
has_many :goal_entries, :primary_key => "code", :foreign_key => "goal_code"
accepts_nested_attributes_for :goal_entries
end
and for GoalEntry :
class GoalEntry < ActiveRecord::Base
attr_accessible :code, :goal_code,
:general_increase_percentage, :general_net_sales,
belongs_to :goal, :primary_key => "code", :foreign_key => "goal_code"
validates_presence_of :code
validates_presence_of :goal_code
validates_uniqueness_of :code , :scope => :goal_code
#validates_numericality_of :general_net_sales
The view to create/edit a GoalEntry for the Goal parent starts like this:
<%= form_for([#goal, #goal_entry]) do |f| %>
<% if #goal_entry.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#goal_entry.errors.count, "error") %>
prohibited this goal_entry from being saved:
</h2>
<ul>
<% #goal_entry.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.hidden_field :goal_code, :required => true%>
</div>
<div class="field">
<%= f.label :code %>
<%= f.number_field :code, :required => true %>
</div>
...
The update method in the goal entries controller:
def update
#goal_entry = GoalEntry.find(params[:id])
respond_to do |format|
if #goal_entry.update_attributes(params[:goal_entry])
format.html { redirect_to edit_goal_path(#goal_entry.goal), notice: 'Goal entry was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #goal_entry.errors, status: :unprocessable_entity }
end
end
end
Entering valid Goal entries works fine. But if there's a validation error message I get the following Message:
**NoMethodError in Goal_entries#update**
ActionView::Template::Error (undefined method `goal_entry_path' for #<#<Class:0x007fce30a0c160>:0x00000004199570>):
1: <%= form_for([#goal, #goal_entry]) do |f| %>
2: <% if #goal_entry.errors.any? %>
3: <div id="error_explanation">
4: <h2><%= pluralize(#goal_entry.errors.count, "error") %>
app/views/goal_entries/_form.html.erb:1:in `_app_views_goal_entries__form_html_erb__827873423371803667_70261777948540'
app/views/goal_entries/edit.html.erb:3:in `_app_views_goal_entries_edit_html_erb__779650500016633360_70261777920720'
app/controllers/goal_entries_controller.rb:77:in `block (2 levels) in update'
app/controllers/goal_entries_controller.rb:72:in `update'
etc...
Is there something wrong with <% if #goal_entry.errors.any? %>
I'll appreciate if somebody has a solution for this. Thanks
Your form_for is generating its route from [#goal, #goal_entry] but you haven't defined #goal in your update action.
Try adding #goal = Goal.find(params[:goal_id]) to your update method.