Rails 4 - Associations - nested attributes - unpermitted parameter - ruby-on-rails
I am trying to make an app in Rails 4. I'm having an awful time of getting basic relationships set up.
I have models for user, profile, vision, personality & qualifications.
The associations are:
User.rb
Profile.rb
belongs_to :user
has_one :personality
accepts_nested_attributes_for :personality
has_many :qualifications
accepts_nested_attributes_for :qualifications, reject_if: :all_blank, allow_destroy: true
has_one :vision
accepts_nested_attributes_for :vision, reject_if: :all_blank, allow_destroy: true
Vision.rb
belongs_to :profile
Personality.rb
belongs_to :profile
Qualifications.rb
belongs_to :profile
The controllers are:
User
class UsersController < ApplicationController
before_action :set_user, only: [:index, :show, :edit, :update, :finish_signup, :destroy]
def index
# if params[:approved] == "false"
# #users = User.find_all_by_approved(false)
# else
#users = User.all
# end
end
# GET /users/:id.:format
def show
# authorize! :read, #user
end
# GET /users/:id/edit
def edit
# authorize! :update, #user
end
# PATCH/PUT /users/:id.:format
def update
# authorize! :update, #user
respond_to do |format|
if #user.update(user_params)
sign_in(#user == current_user ? #user : current_user, :bypass => true)
format.html { redirect_to #user, notice: 'Your profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# GET/PATCH /users/:id/finish_signup
def finish_signup
# authorize! :update, #user
if request.patch? && params[:user] #&& params[:user][:email]
if #user.update(user_params)
#user.skip_reconfirmation!
sign_in(#user, :bypass => true)
redirect_to root_path, notice: 'Your profile was successfully updated.'
# redirect_to [#user, #user.profile || #user.build_profile]
# sign_in_and_redirect(#user, :bypass => true)
else
#show_errors = true
end
end
end
# DELETE /users/:id.:format
def destroy
# authorize! :delete, #user
#user.destroy
respond_to do |format|
format.html { redirect_to root_url }
format.json { head :no_content }
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
# params.require(:user).permit(policy(#user).permitted_attributes)
accessible = [ :first_name, :last_name, :email, :avatar ] # extend with your own params
accessible << [ :password, :password_confirmation ] unless params[:user][:password].blank?
# accessible << [:approved] if user.admin
params.require(:user).permit(accessible)
end
end
Profile
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
after_action :verify_authorized
# GET /profiles
# GET /profiles.json
def index
#profiles = Profile.all
authorize #profiles
end
# GET /profiles/1
# GET /profiles/1.json
def show
end
# GET /profiles/new
def new
#profile = Profile.new
#profile.qualifications.build
#profile.build.vision
#profile.build.personality
#profile.addresses.build
authorize #profile
end
# GET /profiles/1/edit
def edit
end
# POST /profiles
# POST /profiles.json
def create
#profile = Profile.new(profile_params)
authorize #profile
respond_to do |format|
if #profile.save
format.html { redirect_to #profile }
format.json { render :show, status: :created, location: #profile }
else
format.html { render :new }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /profiles/1
# PATCH/PUT /profiles/1.json
def update
# successful = #profile.update(profile_params)
# Rails.logger.info "xxxxxxxxxxxxx"
# Rails.logger.info successful.inspect
# user=#profile.user
# user.update.avatar
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to #profile }
format.json { render :show, status: :ok, location: #profile }
else
format.html { render :edit }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# DELETE /profiles/1
# DELETE /profiles/1.json
def destroy
#profile.destroy
respond_to do |format|
format.html { redirect_to profiles_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_profile
#profile = Profile.find(params[:id])
authorize #profile
end
# Never trust parameters from the scary internet, only allow the white list through.
def profile_params
params.require(:profile).permit(:user_id, :title, :hero, :overview, :research_interest, :occupation, :external_profile,
:working_languages, :tag_list,
user_attributes: [:avatar],
personality_attributes: [:average_day, :fantasy_project, :preferred_style],
vision_attributes: [:id, :profile_id, :long_term, :immediate_challenge],
qualifications_attributes: [:id, :level, :title, :year_earned, :pending, :institution, :_destroy],
addresses_attributes: [:id, :unit, :building, :street_number, :street, :city, :region, :zip, :country, :latitude, :longitude, :_destroy],
industries_attributes: [:id, :sector, :icon] )
end
end
Vision
class VisionsController < ApplicationController
before_action :set_vision, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
# GET /visions
# GET /visions.json
def index
#visions = Vision.all
authorize #visions
end
# GET /visions/1
# GET /visions/1.json
def show
end
# GET /visions/new
def new
#vision = Vision.new
authorize #vision
end
# GET /visions/1/edit
def edit
end
# POST /visions
# POST /visions.json
def create
#vision = Vision.new(vision_params)
authorize #vision
respond_to do |format|
if #vision.save
format.html { redirect_to #vision }
format.json { render :show, status: :created, location: #vision }
else
format.html { render :new }
format.json { render json: #vision.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /visions/1
# PATCH/PUT /visions/1.json
def update
respond_to do |format|
if #vision.update(vision_params)
format.html { redirect_to #vision }
format.json { render :show, status: :ok, location: #vision }
else
format.html { render :edit }
format.json { render json: #vision.errors, status: :unprocessable_entity }
end
end
end
# DELETE /visions/1
# DELETE /visions/1.json
def destroy
#vision.destroy
respond_to do |format|
format.html { redirect_to visions_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_vision
#vision = Vision.find(params[:id])
authorize #vision
end
# Never trust parameters from the scary internet, only allow the white list through.
def vision_params
params[:vision].permit(:profile_id, :long_term, :immediate_challenge)
end
end
Qualifications
class QualificationsController < ApplicationController
before_action :set_qualification, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
# GET /qualifications
# GET /qualifications.json
def index
#qualifications = Qualification.all
authorize #qualifications
end
# GET /qualifications/1
# GET /qualifications/1.json
def show
end
# GET /qualifications/new
def new
#qualification = Qualification.new
authorize #qualification
end
# GET /qualifications/1/edit
def edit
end
# POST /qualifications
# POST /qualifications.json
def create
#qualification = Qualification.new(qualification_params)
authorize #qualification
respond_to do |format|
if #qualification.save
format.html { redirect_to #qualification }
format.json { render :show, status: :created, location: #qualification }
else
format.html { render :new }
format.json { render json: #qualification.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /qualifications/1
# PATCH/PUT /qualifications/1.json
def update
respond_to do |format|
if #qualification.update(qualification_params)
format.html { redirect_to #qualification }
format.json { render :show, status: :ok, location: #qualification }
else
format.html { render :edit }
format.json { render json: #qualification.errors, status: :unprocessable_entity }
end
end
end
# DELETE /qualifications/1
# DELETE /qualifications/1.json
def destroy
#qualification.destroy
respond_to do |format|
format.html { redirect_to qualifications_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_qualification
#qualification = Qualification.find(params[:id])
authorize #qualification
end
# Never trust parameters from the scary internet, only allow the white list through.
def qualification_params
params[:qualification].permit(:profile_id, :level, :title, :year_earned, :pending, :institution)
end
end
Personality
class PersonalitiesController < ApplicationController
before_action :set_personality, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
# GET /personalities
# GET /personalities.json
def index
#personalities = Personality.all
authorize #personalities
end
# GET /personalities/1
# GET /personalities/1.json
def show
end
# GET /personalities/new
def new
#personality = Personality.new
authorize #personality
end
# GET /personalities/1/edit
def edit
end
# POST /personalities
# POST /personalities.json
def create
#personality = Personality.new(personality_params)
authorize #personality
respond_to do |format|
if #personality.save
format.html { redirect_to #personality }
format.json { render :show, status: :created, location: #personality }
else
format.html { render :new }
format.json { render json: #personality.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /personalities/1
# PATCH/PUT /personalities/1.json
def update
respond_to do |format|
if #personality.update(personality_params)
format.html { redirect_to #personality }
format.json { render :show, status: :ok, location: #personality }
else
format.html { render :edit }
format.json { render json: #personality.errors, status: :unprocessable_entity }
end
end
end
# DELETE /personalities/1
# DELETE /personalities/1.json
def destroy
#personality.destroy
respond_to do |format|
format.html { redirect_to personalities_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_personality
#personality = Personality.find(params[:id])
authorize #personality
end
# Never trust parameters from the scary internet, only allow the white list through.
def personality_params
params[:personality].permit( :average_day, :fantasy_project, :preferred_style)
end
end
The profile form has:
<div class="container-fluid">
<div class="row">
<div class="col-xs-10 col-xs-offset-1" >
<%= simple_form_for(#profile, multipart: true) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<div class="intpol2">
About you
</div>
<div class="row">
<div class="col-md-4">
<%= f.input :title, autofocus: true %>
</div>
<div class="col-md-8">
<%= f.input :occupation, :label => "Your occupation or job title" %>
</div>
</div>
<div class="row">
<div class="col-md-6">
<%= render 'users/profileimgform', f: f %>
</div>
<div class="col-md-6">
<%= f.input :hero, as: :file, :label => "Add a background image to your profile page" %>
</div>
</div>
<div class="row">
<div class="col-md-6">
<%= f.input :working_languages, :label => "Select your working languages" %>
</div>
<div class="col-md-6">
<%= f.input :external_profile, :label => "Add a link to your external profile" %>
</div>
</div>
<div class="row">
<div class="col-md-12">
<%= f.input :overview, :label => "Tell us about yourself", :input_html => {:rows => 10} %>
</div>
</div>
<div class="row">
<div class="col-md-12">
<%= render 'industries/industry_selector', f: f %>
</div>
</div>
<div class="row">
<div class="intpol2">
Your professional qualifications
</div>
<%= f.simple_fields_for :qualifications do |f| %>
<%= render 'qualifications/qualification_fields', f: f %>
<% end %>
</div>
<div class="row">
<div class="col-md-6">
<%= link_to_add_association 'Add a qualification', f, :qualifications, partial: 'qualifications/qualification_fields' %>
</div>
</div>
<div class="row">
<div class="intpol2">
Your career
</div>
<%= render 'personalities/form', f: f %>
</div>
<div class="row">
<div class="intpol2">
Your research vision
</div>
</div>
<div class="row">
<div class="intpol2">
Your addresss
</div>
<%= f.simple_fields_for :addresses do |f| %>
<%= render 'addresses/address_fields', f: f %>
<% end %>
</div>
<div class="row">
<div class="col-md-6">
<%= link_to_add_association 'Add an address', f, :addresses, partial: 'addresses/address_fields' %>
</div>
</div>
<div class="form-actions">
<%= f.button :submit, "Submit", :class => 'formsubmit' %>
</div>
<% end %>
</div>
</div>
</div>
</div>
The nested forms are:
Vision
<div class="form-inputs">
<%= f.input :long_term, as: :text, :label => "What is your long term research vision?", :input_html => {:rows => 10} %>
<%= f.input :immediate_challenge, as: :text, :label => "What do you see as the immediate challenge to be addressed in pursuit of your long-term vision?", :input_html => {:rows => 10} %>
</div>
Qualifications
<div class="nested-fields">
<div class="container-fluid">
<div class="form-inputs">
<div class="row">
<div class="col-md-6">
<%= f.input :title, :label => "Your award" %>
</div>
<div class="col-md-6">
</div>
</div>
<div class="row">
<div class="col-md-8">
<%= f.input :pending, :label => "Are you currently studying toward this qualification?" %>
</div>
</div>
<div class="row">
<div class="col-md-4">
<%= f.input :level, collection: [ "Bachelor's degree", "Master's degree", "Ph.D", "Post Doctoral award"] %>
</div>
<div class="col-md-4">
<%= f.input :year_earned, :label => "When did you graduate?", collection: (Date.today.year - 50)..(Date.today.year) %>
</div>
</div>
<div class="row">
<div class="col-md-6">
<%= link_to_remove_association 'Remove this qualification', f %>
</div>
</div>
</div>
</div>
</div>
Personality
<%= f.simple_fields_for :profile do |f| %>
<%= f.simple_fields_for :personality do |ff| %>
<div class="form-inputs">
<%= ff.input :average_day, :label => "What does your day to day work life involve?", as: :text, :input_html => {:rows => 5} %>
<%= ff.input :fantasy_project, :label => "Describe your fantasy project", as: :text, :input_html => {:rows => 5} %>
<%= ff.input :preferred_style, :label => "How do you like to work (distributed teams, on site, easy going about it)?", as: :text, :input_html => {:rows => 5} %>
</div>
<% end %>
<% end %>
The profile show page has:
<div class="intpol3">
<%= #profile.personality.try(:average_day) %>
</div>
<div class="profilesubhead">
My fantasy league research project
</div>
<div class="intpol3">
<%= #profile.personality.try(:fantasy_project) %>
</div>
<div class="profilesubhead">
Working style
</div>
<div class="intpol3">
<%= #profile.personality.try(:preferred_style) %>
</div>
<div class="profilesubhead">
My research vision
</div>
<div class="profilesubhead">
<%= #profile.vision.try(:long_term) %>
</div>
<div class="profilesubhead">
Immediate research challenge in my field
</div>
<div class="profilesubhead">
<%= #profile.vision.try(:immediate_challenge) %>
</div>
The routes are:
resources :profiles, only: [:show, :edit, :update, :destroy]
resources :qualifications
resources :personalities
resources :visions
resources :users do
resources :profiles, only: [:new, :create]
end
From the above, you can see that I have experimented with a variety of different solutions, including using cocoon gem (which I have used for the qualifications model).
Nothing works. The only attributes displaying on the profiles show page are those stored in the profiles table or the users table. None of the models that belong to profile are displaying in the profiles show. Models that have a polymorphic association with profile are displaying.
Currently, when I save and try this, the local host server shows an error called 'Unpermitted parameter: profile'. I can see for qualifications, that the entries in the form are showing in the server, but they do not appear on the profile page. For each of the rest of the nested attributes - none of them display on the profile show page. The rails console shows all of the attributes for personality (with profile id of the user whose profile page is showing are nil).
In my profiles form, I can't even display the vision form fields at all. I thought that might be because I added this:
reject_if: :all_blank, allow_destroy: true
to the accepts nested attributes for :vision in my profile model - but commenting that section out does not improve the situation.
I previously asked this question but could not find any help.
Rails - Displaying associated attributes - unpermitted parameters
I have tried each of the solutions I listed in that post (I think the site point article I attach at the end of that post is the closest thing to relevant that I've been able to find).
I'm out of ideas for things to try to check or change. Can anyone see what I have done wrong?
The server shows the following output for when I try to update the profile by adding entries to the personalities section of the profile form:
Started PATCH "/profiles/8" for ::1 at 2016-01-09 12:38:04 +1100
Processing by ProfilesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Xul2nPMbg/3MnMPoTMtEIFfVg==", "profile"=>{"title"=>"", "occupation"=>"tester", "working_languages"=>"tester", "external_profile"=>"tester", "overview"=>"tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester tester ", "qualifications_attributes"=>{"0"=>{"title"=>"dfds", "pending"=>"0", "level"=>"Master's degree", "year_earned"=>"1967", "_destroy"=>"false", "id"=>"4"}}, "profile"=>{"personality"=>{"average_day"=>"sdf", "fantasy_project"=>"", "preferred_style"=>""}}}, "industry"=>{"sector"=>"5"}, "commit"=>"Submit", "id"=>"8"}
Profile Load (0.3ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."id" = $1 LIMIT 1 [["id", 8]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 9]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 9]]
Unpermitted parameter: profile
(0.1ms) BEGIN
Qualification Load (0.3ms) SELECT "qualifications".* FROM "qualifications" WHERE "qualifications"."profile_id" = $1 AND "qualifications"."id" = 4 [["profile_id", 8]]
(0.1ms) COMMIT
Redirected to http://localhost:3000/profiles/8
Completed 302 Found in 12ms (ActiveRecord: 1.3ms)
TAKING MIHAILS SUGGESTION
I replace the personality form fields with:
<%= f.simple_fields_for :personality do |ff| %>
<div class="form-inputs">
<%= ff.input :average_day, :label => "What does your day to day work life involve?", as: :text, :input_html => {:rows => 5} %>
<%= ff.input :fantasy_project, :label => "Describe your fantasy project", as: :text, :input_html => {:rows => 5} %>
<%= ff.input :preferred_style, :label => "How do you like to work (distributed teams, on site, easy going about it)?", as: :text, :input_html => {:rows => 5} %>
</div>
<% end %>
When I try this, the personality form fields do not render as part of the profiles form at all
TAKING MIHAILS NEXT SUGGESTION
I replace the build personality action in the profiles controller with:
#profile.personality = Personality.new
It was previously: #profile.build.personality
When I try this, I get the same error as previously - I can't see the form fields for personality at all.
TAKING TRH'S SUGGESTION:
If i make the new action:
#profile.build_personality
I still can't see the form for personality fields. No change to the prior attempts
FURTHER UPDATE
It seems the explanation in Mihail's second suggestion is the same effect as TRH's suggestion. Currently, my profiles controller has this new action:
def new
#profile = Profile.new
#profile.qualifications_build
#profile.build_vision
#profile.build_personality
#profile.addresses_build
authorize #profile
end
It's still not working.
TAKING MIHAIL'S NEXT SUGGESTION
def new
#profile = Profile.new
#profile.qualifications_build
#profile.build_vision
#profile.build_personality unless personality
#profile.addresses_build
authorize #profile
end
I still can't see the profile form fields for personality.
SOLUTION:
def new
#profile = Profile.new
#profile.qualifications_build
#profile.build_vision
#profile.build_personality unless #profile.personality
#profile.addresses_build
authorize #profile
end
# GET /profiles/1/edit
def edit
#profile.build_personality unless #profile.personality
end
The problem is in the personality fields partial. If you will look closer on the attributes submitted to server on update, you will see that they are wrong.
"profile"=>{"profile"=>{"personality"=>{"average_day"=>"sdf"}}
So, to fix this you need to check this partial and leave only nested fields block in it.
<%= f.simple_fields_for :personality do |ff| %>
<div class="form-inputs">
<%= ff.input :average_day, :label => "What does your day to day work life involve?", as: :text, :input_html => {:rows => 5} %>
<%= ff.input :fantasy_project, :label => "Describe your fantasy project", as: :text, :input_html => {:rows => 5} %>
<%= ff.input :preferred_style, :label => "How do you like to work (distributed teams, on site, easy going about it)?", as: :text, :input_html => {:rows => 5} %>
</div>
<% end %>
Related
Rails - associations, feedback on users by users - showing feedback
I'm struggling to figure out how to set up my rails evaluation model so that users can use it to leave feedback on other users. I outlined the key part of my problem in this post: Rails - feedback on specific users, how to set up the form to identify relevant users The suggestion I received from that was to set up the model as follows: User.rb has_many :given_evaluations, foreign_key: :evaluator_id, dependent: :destroy, class_name: Evaluation has_many :received_evaluations, foreign_key: :evaluatee_id, dependent: :destroy, class_name: Evaluation Evaluation.rb belongs_to :evaluator, foreign_key: :evaluator_id, class_name: User belongs_to :evaluatee, foreign_key: :evaluatee_id, class_name: User Evaluation Controller class EvaluationsController < ApplicationController before_action :set_evaluation, only: [:show, :edit, :update, :destroy] # before_filter :get_user, only: [:show, :edit, :update, :destroy] # GET /evaluations # GET /evaluations.json def index # #evaluations = Evaluation.all #given_evaluations = current_user.given_evaluations #received_evaluations = current_user.received_evaluations end # GET /evaluations/1 # GET /evaluations/1.json def show # #received_evaluations = #user.received_evaluations #evaluation = current_user.received_evaluations.find_by(id: params[:id]) || current_user.given_evaluations.find(params[:id]) # #received_evaluation = current_user.received_evaluations.find params[:id] end # GET /evaluations/new def new #evaluation = Evaluation.new end # GET /evaluations/1/edit def edit end # POST /evaluations # POST /evaluations.json def create # #evaluation = Evaluation.new(evaluation_params) #evaluation = current_user.given_evaluations.build(evaluation_params) respond_to do |format| if #evaluation.save format.html { redirect_to #evaluation, notice: 'Evaluation was successfully created.' } format.json { render :show, status: :created, location: #evaluation } else format.html { render :new } format.json { render json: #evaluation.errors, status: :unprocessable_entity } end end end # PATCH/PUT /evaluations/1 # PATCH/PUT /evaluations/1.json def update current_user.given_evaluations.find(params[:id]) respond_to do |format| if #evaluation.update(evaluation_params) format.html { redirect_to #evaluation, notice: 'Evaluation was successfully updated.' } format.json { render :show, status: :ok, location: #evaluation } else format.html { render :edit } format.json { render json: #evaluation.errors, status: :unprocessable_entity } end end end # DELETE /evaluations/1 # DELETE /evaluations/1.json def destroy current_user.given_evaluations.find(params[:id]) #evaluation.destroy respond_to do |format| format.html { redirect_to evaluations_url, notice: 'Evaluation was successfully destroyed.' } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_evaluation #evaluation = Evaluation.find(params[:id]) end # Never trust parameters from the scary internet, only allow the white list through. def evaluation_params params[:evaluation].permit(:overall_score, :project_score, :personal_score, :remark, :work_again?, :continue_project?, :evaluatee_id) end end Evaluation form <%= simple_form_for(#evaluation) do |f| %> <%= f.error_notification %> <div class="form-inputs"> <%= f.select :evaluatee_id, User.all.map{|u| [u.formal_name, u.id]} %> <%= f.input :overall_score, collection: 1..10, autofocus: true, :label => "How do you rate this project experience (1 being did not meet expectations - 10 being met all expectations) ?" %> <%= f.input :continue_project?, as: :boolean, checked_value: true, unchecked_value: false, :label => "Do you intend to continue working on the project?" %> <%= f.input :remark, as: :text, :label => "Evaluate your experience", :input_html => {:rows => 10} %> </div> <div class="form-actions"> <%= f.button :submit %> Evaluation show view <% #received_evaluations.each do |receval| %> <div id="portfolioFiltering" class="masonry-wrapper row"> <%= receval.remark %> <%#= eval.personal_score %> <small><%= receval.created_at %></small> </div> <% end %> Alternative attempt at evaluation show view <% #given_evaluations.each do |receval| %> <div id="portfolioFiltering" class="masonry-wrapper row"> <%= receval.remark %> <%#= eval.personal_score %> <small><%= receval.created_at %></small> </div> <% end %> The problem I'm having now is that regardless of whether I try to show given evaluation or received evaluation in the show, I get an error message that says: undefined method `each' for nil:NilClass I can't figure out how to setup the model so that a user can evaluate another user. I want to show each user's received evaluation on their respective show page. i can't figure out what's going wrong. I can see from the console that a user has received evaluations as: => #<Evaluation id: 8, evaluatee_id: 34, overall_score: 4, project_score: 5, personal_score: 5, remark: "jhjkhjhjkhkjhjkhjhkhjhkj", work_again?: nil, continue_project?: nil, created_at: "2016-06-12 21:52:53", updated_at: "2016-06-12 21:52:53", evaluator_id: 34> There is an entry for the user I'm working with. However, I can't find a way to show that evaluation.
The most immediate problem I could see is that the instance variables: #given_evaluations and #received_evaluations are not being set in your show actions and the commented portions are using the ActiveRecord#find method, which would return one instance therefore the reason you couldn't loop through the evaluations. I think a better place to show all your user's evaluation is in the index action, as you're doing already, you may move the current logic for the show to the index view. To show each user's received evaluation on the show action, you would do: #evaluation = current_user.received_evaluations.find(params[:id]) Then in your show view, you should have something like: <div id="portfolioFiltering" class="masonry-wrapper row"> <%= #evaluation.remark %> <%= #evaluation.personal_score %> <small><%= #evaluation.created_at %></small> </div>
Rails 4 - nested attributes with Cocoon gem
I'm trying to make an app with Rails 4. I'm using Cocoon gem for nested forms with simple form. I have a qualifications model, nested in a profile model. The associations are: Qualifications belongs_to :profile Profile has_many :qualifications accepts_nested_attributes_for :qualifications, reject_if: :all_blank, allow_destroy: true In my profile form, I incorporate the qualifications attributes with: <%= f.simple_fields_for :qualifications do |f| %> <%= render 'qualifications/qualification_fields', f: f %> <% end %> In my Qualifications form attributes, I have: <div class="nested-fields"> <div class="container-fluid"> <div class="form-inputs"> <div class="row"> <div class="col-md-6"> <%= f.input :title, :label => "Your award" %> </div> <div class="col-md-6"> </div> </div> <div class="row"> <div class="col-md-8"> <%= f.input :pending, :label => "Are you currently studying toward this qualification?" %> </div> </div> <div class="row"> <div class="col-md-4"> <%= f.input :level, collection: [ "Bachelor's degree", "Master's degree", "Ph.D", "Post Doctoral award"] %> </div> <div class="col-md-4"> <%= f.input :year_earned, :label => "When did you graduate?", collection: (Date.today.year - 50)..(Date.today.year + 5) %> </div> </div> <div class="row"> <div class="col-md-6"> <%= link_to_remove_association 'Remove this qualification', f %> </div> </div> </div> </div> </div> In my profiles controller I have: def new #profile = Profile.new #profile.qualifications_build #profile.build_vision #profile.build_personality #profile.addresses_build authorize #profile end # GET /profiles/1/edit def edit #profile.build_personality unless #profile.personality #profile.qualifications_build unless #profile.qualifications #profile.build_vision unless #profile.vision #profile.addresses_build unless #profile.addresses end # POST /profiles # POST /profiles.json def create #profile = Profile.new(profile_params) authorize #profile respond_to do |format| if #profile.save format.html { redirect_to #profile } format.json { render :show, status: :created, location: #profile } else format.html { render :new } format.json { render json: #profile.errors, status: :unprocessable_entity } end end end # PATCH/PUT /profiles/1 # PATCH/PUT /profiles/1.json def update # successful = #profile.update(profile_params) # Rails.logger.info "xxxxxxxxxxxxx" # Rails.logger.info successful.inspect # user=#profile.user # user.update.avatar respond_to do |format| if #profile.update(profile_params) format.html { redirect_to #profile } format.json { render :show, status: :ok, location: #profile } else format.html { render :edit } format.json { render json: #profile.errors, status: :unprocessable_entity } end end end # DELETE /profiles/1 # DELETE /profiles/1.json def destroy #profile.destroy respond_to do |format| format.html { redirect_to profiles_url } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_profile #profile = Profile.find(params[:id]) authorize #profile end # Never trust parameters from the scary internet, only allow the white list through. def profile_params params.require(:profile).permit( :title, :hero, :overview, :research_interest, :occupation, :external_profile, :working_languages, :tag_list, industry_ids: [], user_attributes: [:avatar], personality_attributes: [:average_day, :fantasy_project, :preferred_style], vision_attributes: [ :long_term, :immediate_challenge], qualifications_attributes: [ :level, :title, :year_earned, :pending, :institution, :_destroy], addresses_attributes: [:id, :unit, :building, :street_number, :street, :city, :region, :zip, :country, :time_zone, :latitude, :longitude, :_destroy], industries_attributes: [:id, :sector, :icon] ) end end My problems are: when I save all of this and try it, each time I update the profile, it creates a new entry for the qualifications, so the list of qualifications displayed duplicates on every update (even when no update is made to the fields). When I update the profile form and click remove qualification, it does not get removed from the array. I can delete them in the console, but not using the form. I'm worried that if I remove #profile.qualifications_build unless #profile.qualifications from the edit action in the profile controller, I won't be able to update those attributes. Also, it doesn't make sense to me that removing that would solve the problem with removing qualifications. Do I need to add something to the remove qualification link to force it to remove the entry?
I think you just forgot to add :id in the allowed parameters for qualifications, in your profile_params: qualifications_attributes: [ :id, :level, :title, :year_earned, :pending, :institution, :_destroy] Without an ID to match, any qualification will be seen as a new one.
Rails link_to path
I am trying to make an app in Rails 4. I have 3 models: Project, Project_Question, Project_Answer The associations are: Project: has_many :project_questions and accepts nested attributes for project questions. Project Question: belongs_to :project has_one :project_answer and accepts nested attributes for Project Answers. My routes are nested as follows: resources :projects do resources :project_questions do resources :project_answers end end In my Project Questions partial, I want a link to answer the question. I've tried: <%= link_to 'Answer this question', new_project_project_question_project_answer_path(:project_question_id => project_question.id) %> I have a route with that name in my routes file, but I'm getting this error message: undefined local variable or method `project_question' for #<#<Class:0x0000010742b9d8>:0x0000010f810b68> What should go in the brackets? View: <div class="containerfluid"> <div class="row"> <div class="col-md-10 col-md-offset-1"> <% #project.project_questions.each do |singleQuestion| %> <div class="categorytitle"> <%= singleQuestion.title %> </div> <div class="generaltext"> <%= singleQuestion.try(:content) %> </div> <span class="editproject"> <% if current_user.id == #project.creator_id %> <%= link_to 'Answer this question', new_project_project_questions_project_answer_path(:project_question_id => project_question.id) %> <% end %> </span> <% end %> </div> </div> </div> Project Question controller: class ProjectQuestionsController < ApplicationController before_action :set_project_question, only: [:show, :edit, :update, :destroy] # GET /project_questions # GET /project_questions.json def index #project_questions = ProjectQuestion.all end # GET /project_questions/1 # GET /project_questions/1.json def show end # GET /project_questions/new def new #project_question = ProjectQuestion.new #project = Project.find(params[:project_id]) # #project_id = params[:project_id] #project_question.project_answers[0] = ProjectAnswer.new end # GET /project_questions/1/edit def edit end # POST /project_questions # POST /project_questions.json def create #project_question = ProjectQuestion.new(project_question_params) #project_question.project_id = project_question_params[:project_id] respond_to do |format| if #project_question.save format.html { redirect_to project_url(Project.find(project_question_params[:project_id])), notice: 'Project question was successfully created.' } format.json { render action: 'show', status: :created, location: #project_question } else format.html { render action: 'new' } format.json { render json: #project_question.errors, status: :unprocessable_entity } end end end # PATCH/PUT /project_questions/1 # PATCH/PUT /project_questions/1.json def update respond_to do |format| if #project_question.update(project_question_params) format.html { redirect_to #project_question, notice: 'Project question was successfully updated.' } format.json { head :no_content } else format.html { render action: 'edit' } format.json { render json: #project_question.errors, status: :unprocessable_entity } end end end # DELETE /project_questions/1 # DELETE /project_questions/1.json def destroy #project_question.destroy respond_to do |format| format.html { redirect_to project_questions_url } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_project_question #project_question = ProjectQuestion.find(params[:id]) end # Never trust parameters from the scary internet, only allow the white list through. def project_question_params params[:project_question].permit(:id, :title, :content, :project_id, :user_id, project_answer_atttibutes: [:id, :answer, :project_question_id, :user_id] ) end end
When you run rake routes, you will find this one new_project_project_question_project_answer GET /projects/:project_id/project_questions/:project_question_id/project_answers/new(.:format) project_answers#new That means it requires :project_id and :project_question_id as keys. This should work <%= link_to 'Answer this question', new_project_project_question_project_answer_path(:project_id => #project.id, :project_question_id => singleQuestion.id) %> Notice new_project_project_question_project_answer_path not new_project_project_questions_project_answer_path
Your link_to should be something below <%= link_to 'Answer this question', new_project_project_questions_project_answer_path(:project_id => #project.id, :project_question_id => singleQuestion.id) %> View will look like below <div class="containerfluid"> <div class="row"> <div class="col-md-10 col-md-offset-1"> <% #project.project_questions.each do |singleQuestion| %> <div class="categorytitle"> <%= singleQuestion.title %> </div> <div class="generaltext"> <%= singleQuestion.try(:content) %> </div> <span class="editproject"> <% if current_user.id == #project.creator_id %> <%= link_to 'Answer this question', new_project_project_questions_project_answer_path(:project_question_id => singleQuestion.id) %> <% end %> </span> <% end %> </div> </div> </div> Check that params def project_question_params params[:project_question].permit(:id, :title, :content, :project_id, :user_id, project_answer_atttibutes: [:id, :answer, :project_question_id, :user_id] ) end There is project_id and you did not pass it in link_to so it thwos new errormissing required keys: [:project_id]
Rendering partial in Rails - nested objects
I am trying to render a partial in my rails app. I have done this successfully with other partials, but the difference between those that worked and this one is that this partial belongs to a nested model. It is nested inside the model's view that I am trying to use to render the partial. I have two models, Project and Project_question. Project has many project questions and accepts nested attributes for project question. Project question belongs to Project. My routes are: resources :projects do resources :project_questions do resources :project_answers end end My project question controller has: class ProjectQuestionsController < ApplicationController before_action :set_project_question, only: [:show, :edit, :update, :destroy] # GET /project_questions # GET /project_questions.json def index #project_questions = ProjectQuestion.all end # GET /project_questions/1 # GET /project_questions/1.json def show end # GET /project_questions/new def new #project_question = ProjectQuestion.new #project = Project.find(params[:project_id]) # #project_id = params[:project_id] #project_question.project_answers[0] = ProjectAnswer.new end # GET /project_questions/1/edit def edit end # POST /project_questions # POST /project_questions.json def create #project_question = ProjectQuestion.new(project_question_params) #project_question.project_id = project_question_params[:project_id] respond_to do |format| if #project_question.save format.html { redirect_to project_url(Project.find(project_question_params[:project_id])), notice: 'Project question was successfully created.' } format.json { render action: 'show', status: :created, location: #project_question } else format.html { render action: 'new' } format.json { render json: #project_question.errors, status: :unprocessable_entity } end end end # PATCH/PUT /project_questions/1 # PATCH/PUT /project_questions/1.json def update respond_to do |format| if #project_question.update(project_question_params) format.html { redirect_to #project_question, notice: 'Project question was successfully updated.' } format.json { head :no_content } else format.html { render action: 'edit' } format.json { render json: #project_question.errors, status: :unprocessable_entity } end end end # DELETE /project_questions/1 # DELETE /project_questions/1.json def destroy #project_question.destroy respond_to do |format| format.html { redirect_to project_questions_url } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_project_question #project_question = ProjectQuestion.find(params[:id]) end # Never trust parameters from the scary internet, only allow the white list through. def project_question_params params[:project_question].permit(:id, :title, :content, :project_id, :user_id, project_answer_atttibutes: [:id, :answer, :project_question_id, :user_id] ) end end My project question form has: <%= simple_form_for [#project, #project_question] do |f| %> <%= f.input :project_id, as: :hidden, input_html: {value: #project.id} %> <%= f.input :title, label: 'Question:', :label_html => {:class => 'question-title'}, placeholder: 'Type your question here', :input_html => {:style => 'width: 100%', :rows => 4, class: 'response-project'} %> <%= f.input :content, label: 'Is there any context or other information?', :label_html => {:class => 'question-title'}, placeholder: 'Context might help to answer your question', :input_html => {:style => 'width: 100%', :rows => 5, class: 'response-project'} %> <br><br><br> <%= f.button :submit, 'Send!', :class => "cpb" %> My project question partial has: </div> <div class="generaltext"> <%= #project.project_question.try(:content) %> </div> Inside my project#show, I try to render the protect question partial as follows: <%= render 'project_questions/pqps' %> and the project_questions/_pqps.html.erb file contains: <div class="containerfluid"> <div class="row"> <div class="col-md-10 col-md-offset-1"> <div class="categorytitle"> <%= #project.project_question.title %> </div> <div class="generaltext"> <%= #project.project_question.try(:content) %> </div> <span class="editproject"> <% if current_user.id == #project.creator_id %> <%= link_to 'Answer this question', new_project_question_project_answer_path(:project_quesetion_id => #project_question.id) %> <% end %> </span> </div> </div> </div> The error message I get when I try is: undefined method `project_question' for #<Project:0x0000010d8bad40>
My problem was the has_many relationship between project and project questions. I had to make a loop to show each question. This worked for me: <% #project.project_questions.each do |singleQuestion| %> <div class="categorytitle"> <%= singleQuestion.title %> </div> <div class="generaltext"> <%= singleQuestion.try(:content) %> </div>
Why is my Rails hidden_field not picking up any value when all other fields work fine?
I have a model for a "timeline_event" which belongs_to a sales_opportunity. For example it could be "call the prospect" or similar, with a due_date, activity_details (optional), and a checkbox to say whether it's complete or not. This works fine. I can add new timeline_events to the database via a modal on the sales_opportunity page, which also works fine. However when I try to edit the timeline_event (via the timeline_events edit controller action) the hidden_field for sales_opportunity_id refuses to pick up the value (it has no value whatsoever). Even when I try to force a value: <%= f.hidden_field :sales_opportunity_id, :value => #sales_opportunity.id %> It won't add any value to the hidden_field. I've tried moving the field around within the form group, but nothing seems to work. I have other forms that are similar in my model that work fine, but for some reason this isn't working - can anyone help please? My Form: <%= form_for(#timeline_event, :html => {:class => "form-horizontal"}) do |f| %> <div class="form-group"> <%= f.hidden_field :sales_opportunity_id %> <%= f.label :activity, :class => "col-md-4 control-label" %> <div class ="col-md-8"> <%= f.text_field :activity, :placeholder => "Enter activity details" %> </div> </div> <div class="form-group"> <%= f.label :due_date, :class => "col-md-4 control-label" %> <div class ="col-md-8"> <div class='input-group date' id='datetimepicker' data-date-format="YY.MM.DD"> <%= f.text_field :due_date, class: "form-control", data: { date_format: 'YYYY/MM/DD' }, :placeholder => "YYYY/MM/DD" %> <span class="input-group-addon"> <span class="glyphicon glyphicon-calendar"></span> </span> </div> </div> </div> <div class="form-group"> <%= f.label :event_notes, 'Activity Details', :class => "col-md-4 control-label" %> <div class ="col-md-8"> <%= f.text_area(:event_notes, :placeholder => "Put any notes about the activity here") %> </div> </div> <div class="col-md-6 col-md-offset-4"> <div class="checkbox input"> <label> <%= f.check_box :completed %> Task Completed? </label> </div> <br> </div> <%= #sales_opportunity.id %> <%= f.submit "Save", class: "btn btn-large btn-success" %> <% end %> *Note - I include the line <%= #sales_opportunity.id %> here and this does output the correct sales_opportunity_id for the timeline_event. All other fields (activity, due_date, completed) are populated with the information of the timeline_event I'm trying to edit. Timeline_Events_Controller: class TimelineEventsController < ApplicationController before_action :set_timeline_event, only: [:show, :edit, :update, :destroy] before_action :signed_in_user, only: [:edit, :update, :show, :index] before_action :correct_org, only: [:edit, :update, :show, :index] # GET /timeline_events # GET /timeline_events.json def index #timeline_events = TimelineEvent.all end # GET /timeline_events/1 # GET /timeline_events/1.json def show end # GET /timeline_events/new def new #timeline_event = TimelineEvent.new(sales_opportunity_id: params[:sales_opportunity_id]) end # GET /timeline_events/1/edit def edit #timeline_event = TimelineEvent.find(params[:id]) #sales_opportunity = #timeline_event.sales_opportunity end # POST /timeline_events # POST /timeline_events.json def create #timeline_event = TimelineEvent.new(timeline_event_params) #sales_opportunity = #timeline_event.sales_opportunity respond_to do |format| if #timeline_event.save format.html { redirect_to #timeline_event.sales_opportunity, :flash => {:success => 'Timeline event was successfully created.'} } format.json { render :show, status: :created, location: #timeline_event } format.js else format.html { render :new } format.json { render json: #timeline_event.errors, status: :unprocessable_entity } format.js { render json: #sales_opportunity.errors, status: :unprocessable_entity } end end end # PATCH/PUT /timeline_events/1 # PATCH/PUT /timeline_events/1.json def update respond_to do |format| if #timeline_event.update(timeline_event_params) format.html { redirect_to #timeline_event.sales_opportunity, :flash => {:success => 'Timeline event was successfully updated.'} } format.json { render :show, status: :ok, location: #timeline_event } else format.html { render :edit } format.json { render json: #timeline_event.errors, status: :unprocessable_entity } end end end # DELETE /timeline_events/1 # DELETE /timeline_events/1.json def destroy #timeline_event.destroy respond_to do |format| format.html { redirect_to timeline_events_url, :flash => {:success => 'Timeline event was successfully destroyed.'} } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_timeline_event #timeline_event = TimelineEvent.find(params[:id]) end def timeline_event_params params.require(:timeline_event).permit(:sales_opportunity_id, :due_date, :activity, :completed, :event_notes) end #before filters def signed_in_user unless signed_in? store_location redirect_to signin_url, notice: "Please sign in." unless signed_in? end end def correct_org timeline_org = #timeline_event.sales_opportunity.user.organization #user = current_user if #user.organization_id == timeline_org.id else redirect_to root_url, notice: "You are not permitted to visit that page. Please create an account or sign in" end end end My timeline_event model: class TimelineEvent < ActiveRecord::Base belongs_to :sales_opportunity validates :activity, presence: true validates :due_date, presence: true validates :sales_opportunity_id, presence: true end I can only imagine this is some syntax error or other stupid error on my part, because I just can't see anything wrong with the code (it's identical to all other forms on my site). Can anyone spot where I'm going wrong here please? Thanks!
I disagree with #smathy as you indeed can explicitly pass a value to a hidden_field tag like you have done. However, that's not even necessary because you are doing a circular assignment. By that I mean you are declaring #sales_opportunity and assigning it the value of #timeline_event.sales_opportunity in the edit action of your controller. You then go into the view and try to assign #timeline_event.sales_opportunity back to #sales_opportunity. That's circular logic. If the sales_opportunity for the timeline_event you are editing is already present, and you aren't wanting to allow the user to edit it, why include it in the form at all? Delete the #sales_opportunity instance variable from the edit action in the controller, and just remove the hidden field entirely from the view. See if doing that makes it work as desired.
The form_for field helpers don't work like that, they don't take a :value key in the options. Basically the name of the field must correspond to a method on the object you've passed into form_for (in your case #timeline_event), and that's where the form will get the value from. If you want to provide your own field and value, then use the _tag family of helpers, eg.: <%= hidden_field_tag :name_of_field, #sales_opportunity.id %> Or use f.fields_for to create a sub-field (usually in combination with accepts_nested_attributes_for)