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.
Related
(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
I am stuck up with building nested forms using Ruby on Rails.
I am trying to build a form which has fields from three tables(User, Contact, Address). User table has address_id and contact_id. When user fills the details, contact details should be saved in contact table and address should be saved in address table. Both the ids should get saved in user table along with the user details. How should I proceed?
My model,
class Address < ApplicationRecord
has_one :user
end
class Contact < ApplicationRecord
has_one :user
end
class User < ApplicationRecord
belongs_to :address
belongs_to :contact
end
My controller,
class UsersController < ApplicationController
def new
#user = User.new
#user.build_contact
#user.build_address
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
params.require(:user).permit(:name, :email, contact_attributes: [:phone], address_attributes: [:street, :city])
end
end
And my view is,
<%= form_for(user) do |f| %>
<% if user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<%= f.fields_for :contact do |c| %>
<div class="field">
<%= c.label :phone %>
<%= c.text_field :phone %>
</div>
<% end %>
<%= f.fields_for :address do |a| %>
<div class="field">
<%= a.label :street %>
<%= a.text_field :street %>
</div>
<div class="field">
<%= a.label :city %>
<%= a.text_field :city %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Is my approach right? Kindly please suggest. Thanks in advance.
You missing a couple of lines...
class User < ApplicationRecord
belongs_to :address
belongs_to :contact
accepts_nested_attributes_for :address
accepts_nested_attributes_for :contact
end
Also ensure you accept :id and :_delete
params.require(:user).permit(:name, :email, contact_attributes: [:id, :phone, :_delete], address_attributes: [:id, :street, :city, :_delete]
Following the DRY rule, I've inserted the render partial command inside my officers\_form.html.erb view:
<%= form_for(officer) do |f| %>
<% if officer.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(officer.errors.count, "error") %> prohibited this officer from being saved:</h2>
<ul>
<% officer.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= render :partial => 'users/form', :locals => {:user => #officer.user} %>
<%= render :partial => 'addresses/form', :locals => {:address => #officer.address} %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
This is my users\_form.html.erb file:
<%= form_for(user) do |f| %>
<% if user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.fields_for :user do |user_fields| %>
<div class="field">
<%= user_fields.label :last_name %>
<%= user_fields.text_field :last_name %>
</div>
<div class="field">
<%= user_fields.label :first_name %>
<%= user_fields.text_field :first_name %>
</div>
<div class="field">
<%= user_fields.label :middle_name %>
<%= user_fields.text_field :middle_name %>
</div>
<div class="field">
<%= user_fields.label :gender %>
<%= user_fields.select(:gender, User.genders.keys) %>
</div>
<% end %>
<!--div class="actions"-->
<!--%= f.submit %-->
<!--/div-->
<% end %>
Same reasoning as for User code applies to Addresses code, so I'll omit here for shortness.
This is my officers_controller file:
class OfficersController < BaseController
before_action :set_officer, only: [:show, :edit, :update, :destroy]
# GET /officers
# GET /officers.json
def index
#officers = Officer.all
end
# GET /officers/1
# GET /officers/1.json
def show
end
# GET /officers/new
def new
#officer = Officer.new
end
# GET /officers/1/edit
def edit
end
# POST /officers
# POST /officers.json
def create
#officer = Officer.new(officer_params)
respond_to do |format|
if #officer.save
format.html { redirect_to #officer, notice: 'Officer was successfully created.' }
format.json { render :show, status: :created, location: #officer }
else
format.html { render :new }
format.json { render json: #officer.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /officers/1
# PATCH/PUT /officers/1.json
def update
respond_to do |format|
if #officer.update(officer_params)
format.html { redirect_to #officer, notice: 'Officer was successfully updated.' }
format.json { render :show, status: :ok, location: #officer }
else
format.html { render :edit }
format.json { render json: #officer.errors, status: :unprocessable_entity }
end
end
end
# DELETE /officers/1
# DELETE /officers/1.json
def destroy
#officer.destroy
respond_to do |format|
format.html { redirect_to officers_url, notice: 'Officer was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_officer
#officer = Officer.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def officer_params
#params.fetch(:officer, {})
params.require(:officer).permit!
end
end
Now if I go to http://localhost:3000/officers/new, the parts included in both the users and addresses forms are shown, but when I press the Create officer button nothing happens. Where is the error?
class Officer < ApplicationRecord
belongs_to :manager#, inverse_of: :officer
has_many :customers#, inverse_of: :officer
has_one :user, as: :userable, dependent: :destroy
has_one :address, as: :addressable, dependent: :destroy
accepts_nested_attributes_for :user, :address
end
class Manager < ApplicationRecord
has_many :officers#, inverse_of: :manager
has_one :user, as: :userable, dependent: :destroy
has_one :address, as: :addressable, dependent: :destroy
accepts_nested_attributes_for :user, :address
end
class User < ApplicationRecord
enum gender: { female: 0, male: 1, undefined: 2 }
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :userable, polymorphic: true
end
Thanks,
FZ
You have not set user_attributes in your officer_params, do this:
def officer_params
#params.fetch(:officer, {})
params.require(:officer).permit(:id, user_attributes: [:id, :last_name, :middle_name, :first_name, :gender, :_destroy])
end
And also change accepts_nested_attributes_for :user, :address
to
'accepts_nested_attributes_for :user, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :address, reject_if: :all_blank, allow_destroy: true'
And you need to address_attributes to your officer params aswell but since i don't know your database field i can't do that part for you but it's pretty much the same as the user_attributes but with different fields(except :id and :_destroy which are the same for all).
EDIT:
This is a nested form:
<%= form_for(officer) do |f %>
<%= f.fields_for :user do |user| %>
<%= user.text_field :last_name %>
<%= user.text_field :middle_name %>
<%= user.text_field :first_name %>
<% end %>
<%= f.fields_for :address do |address| %>
<%= address.text_field :street_name %>
<%= address.text_field :zip_code %>
<% end %>
<%= f.submit 'submit' %>
This way one submit button supplies for all the nested forms aswell.
What you have is this:
<%= form_for(officer) do |f %>
<%= form_for(user) do |f|
<%= f.fields_for :user do |user| %> // this (f) now stands for the user form instead of the officer form
<%= user.text_field :last_name %>
<%= user.text_field :middle_name %>
<%= user.text_field :first_name %>
<% end %>
<% end %>
<%= form_for(address) do |f| %>
<%= f.fields_for :address do |address| %> // same for this one
<%= address.text_field :street_name %>
<%= address.text_field :zip_code %>
<% end %>
<% end %>
<%= f.submit 'submit' %>
Now you don't have a nested form, you just have 3 different full forms and you can't submit multiple forms with one submit button this way.
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'm trying to add a fields_for attribute to a nested rails form. Any time I try to create a new object, it returns
Message(#58819400) expected, got
Array(#18903800) ...
app/controllers/discussions_controller.rb:53:in
`create'
If I try to access nested fields_for from forms based on non-nested resources (aka "form_for #parent" instead of "form_for [#parent, #child]" it works fine. Code below - any help with this really appreciated.
Controller:
# GET /discussions/new
# GET /discussions/new.xml
def new
#forum = Forum.find(params[:forum_id])
#discussion = Discussion.new
#discussion.messages.build
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #discussion }
end
end
def create
#forum = Forum.find_by_id(params[:forum_id])
#discussion = #forum.discussions.new(params[:discussion])
#discussion.user = current_user
respond_to do |format|
if #discussion.save
format.html { redirect_to([#forum, #discussion], :notice => 'Discussion was successfu#ly created.') }
format.xml { render :xml => [#forum, #discussion], :status => :created, :location => #discussion }
else
format.html { render :action => "new" }
format.xml { render :xml => #discussion.errors, :status => :unprocessable_entity }
end
end
end
Models:
class Forum < ActiveRecord::Base
belongs_to :user
has_many :discussions, :dependent => :destroy
validates :title, :presence => true
accepts_nested_attributes_for :discussions, :allow_destroy => true
end
class Discussion < ActiveRecord::Base
belongs_to :user
belongs_to :forum
has_many :messages, :dependent => :destroy
validates :title, :presence => true
end
class Message < ActiveRecord::Base
belongs_to :user
validates :user, :presence => true
validates :content, :presence => true
end
The view:
<%= form_for [#forum, #discussion] do |f| %>
<% if #discussion.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#discussion.errors.count, "error") %> prohibited this discussion from being saved:</h2>
<ul>
<% #discussion.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>
<%= f.fields_for :messages do |builder| %>
<%= builder.label :content, "Message" %>
<%= builder.text_area :content, :rows => 10 %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And finally, the routing:
resources :forums do
resources :discussions do
resources :messages
end
end
Any help with this really appreciated - I'm completely stumped.
Arghhh - really sorry folks...I just realised I'd forgot the accepts_nested_attributes_for in the discussions model, & consequently forums could access discussions, but discussions couldn't get down to messages.
Thanks anyhow.