ActiveModel::MassAssignmentSecurity::Error even when using accepts_nested_attributes_for - ruby-on-rails

My complete error message is:
ActiveModel::MassAssignmentSecurity::Error in
WorkoutsController#create Can't mass-assign protected attributes:
workout_entry
The params that I am sending looks like:
{"workout"=>{"unit"=>"kg", "name"=>"2013-02-20T21:26:19", "note"=>nil, "workout_entry"=> [{"workout_entry_number"=>"1", "exercise_id"=>2, "entry_detail"=>[{"set_number"=>"1", "weight"=>"32", "reps"=>"43"}]}]}}
I have a workout that has many workout entries and each workout entries can have many entry details. The note is optional.
workout.rb
class Workout < ActiveRecord::Base
has_many :workout_entries, dependent: :destroy
attr_accessible :id, :name, :note, :unit, :workout_entries_attributes
belongs_to :user
accepts_nested_attributes_for :workout_entries
validates_presence_of :name
validates_presence_of :unit, :inclusion => %w(kg lb)
validates_associated :workout_entries
default_scope order("created_at DESC")
end
workout_entry.rb
class WorkoutEntry < ActiveRecord::Base
belongs_to :workout
belongs_to :exercise
has_many :entry_details, dependent: :destroy
attr_accessible :workout_id, :exercise_id, :workout_entry_number, :entry_details_attributes
accepts_nested_attributes_for :entry_details
validates :exercise_id, presence: true, numericality: {only_integer: true}, :inclusion => { :in => 1..790 }
validates :workout_id, presence: true, numericality: {only_integer: true, greater_than_or_equal_to: 1}
validates :workout_entry_number, presence: true, numericality: {only_integer: true, greater_than_or_equal_to: 1}
end
workouts_controller.rb
class WorkoutsController < ApplicationController
respond_to :json
before_filter :authenticate_user!
def index
respond_with(current_user.workouts)
end
def show
respond_with(current_user.workouts.find(params[:id]))
end
def create
respond_with(current_user.workouts.create(params[:workout]))
end
def update
#workout = current_user.workouts.find(params[:id])
if #workout.update_attributes(params[:workout])
render json: #workout, status: :ok
else
render json: #workout.errors, status: :unprocessable_entity
end
end
def destroy
respond_with(current_user.workouts.destroy(params[:id]))
end
end
I tried switching the ordering of attr_accessible and accepts_nested_attributes_for within the workout.rb, but it does not work.
I even tried to set
config.active_record.whitelist_attributes = true
but creating was still prevented.

accepts_nested_attributes_for does not add any attributes to the whitelist. Whatever keys your trying to pass to update_attributes have to be listed in attr_accessible, in your case you need to add workout_entry to attr_accessible.
It does look like you have an error in the form, if your using fields_for then it should be using the key workout_entries_attributes, which you have accessible.

Try to add workout_entry_ids in attr accessible in your workout model.

I decided to not use accepts_nested_attributes_for in the workout and workout_entry models because it wasn't working for me. I also updated the format of my json that is sent. Details are in the link below
link

Related

Why throwing abort in a nested association raises “Failed to destroy the record” exception?

I need to validate if any CameraVectors has been associated to any MonitoredPlace before I destroy a Camera.
Camera's Model
class Camera < ApplicationRecord
belongs_to :location
has_many :camera_vectors, inverse_of: :camera, dependent: :destroy
validates :description, :device_serial, :device_name,
:device_type, :device_api_url, :device_user, :device_password,
presence: true
accepts_nested_attributes_for :camera_vectors, allow_destroy: true
end
CameraVector's model
class CameraVector < ApplicationRecord
belongs_to :camera, inverse_of: :camera_vectors
belongs_to :monitored_place, optional: true
validates :description, presence: true
validates :position, numericality: { greater_than_or_equal_to: 0 }, presence: true
before_destroy :has_monitored_place?
private
def has_monitored_place?
if monitored_place.present?
errors.add(:base, "cannot delete")
throw :abort
end
end
end
MonitoredPlace's model
class MonitoredPlace < ApplicationRecord
belongs_to :location
belongs_to :place_type
has_many :camera_vectors
validates :place_name, presence: true
validates :place_type_id, uniqueness: { scope: :location_id }, presence: true
scope :enabled, -> { where.not(enabled_on: nil).where(disabled_on: nil) }
end
Because of the accepts_nested_attributes_for whenever I try to update or destroy a camera this nested fields are sent as params
"camera_vectors_attributes"=>{"0"=>{"description"=>"A", "position"=>"1", "_destroy"=>"1", "id"=>"47"}}
I thought If I wrote a callback before_destroy in the model CameraVector I could validate it, but if the validation occurs it raises ActiveRecord::RecordNotDestroyed in the controller.
if #camera.destroy(camera_params)
redirect_to(action: :index, notice: t(".success"))
else
render :index
end
as you can read in the api documentation
ActiveRecord::RecordNotDestroyed
Raised by ActiveRecord::Base#destroy! when a call to #destroy would return false.
It is result of
before_destroy :has_monitored_place?
that calls a method and returns false.
def has_monitored_place?
if monitored_place.present?
errors.add(:base, "cannot delete")
throw :abort
end
end
to change this behavior implement a logic similar to the one described in the api
begin
complex_operation_that_internally_calls_destroy!
rescue ActiveRecord::RecordNotDestroyed => invalid
puts invalid.record.errors
end
or read
How do I 'validate' on destroy in rails

NameError in RequestsController#create, undefined local variable or method `request_params' for #<RequestsController:0x5635d28>

I'm having an issue with my rails application. Basically I'm trying to make a simple website where people can apply for jobs. When the user clicks "show" on the jobs page (the page consists of a table of job offers)to show a particular job offer they are brought to a new page where more information is given on the job, and a text box where the person can write a message to make a request for the job.(I plan to make this a more efficient method such as a link to the candidates page but for the moment I'm keeping it as a simple text box where the candidate can comment). The problem arises when the candidate tries to submit the comment, I get the error mentioned above. Could it be an issue in the models as there is a number of relationships set up, i.e. a job has many requests, requests has many candidates etc, etc. I've gotten this message before and it was simply a case of fixing a variable name mistake, however I've been looking over this for a while, had it proof read by a friend and I still can't find the error.I would greatly appreciate any help!
RequestsController
class RequestsController < ApplicationController
before_action :authorise
#set_request, only: [:show, :edit, :update, :destroy]
def create
#job = Job.find params[:job_id]
#request = #job.requests.new(request_params) <- Error highlights this line
#request.candidate_id = #current_candidate.id #sets the user_id FK
#request.save #saves the #comment
# object to the comments table
respond_to do |format|
format.html{redirect_to #job}
end
end
private
def comment_params
#This is the method ehich whitelists the data fields from the format
params.require(:request).permit(:content, :job_id, :candidate_id)
end
end
Request Model
class Request < ActiveRecord::Base
belongs_to :job, dependent: :destroy
has_many :candidates
end
Candidate Model
class Candidate < ActiveRecord::Base
has_secure_password
validates_uniqueness_of:can_email
belongs_to :request
validates :can_name, presence: true
validates :can_surname, presence: true
validates :college, presence: true
validates :can_email, presence: true
validates :address, presence: true
validates :experience, presence: true
validates :password_digest, presence: true
validates :college_year, numericality: { only_integer: true }
end
Job Model
class Job < ActiveRecord::Base
belongs_to :sector
has_many :requests, dependent: :destroy
validates :name, presence: true
validates :employer, presence: true
validates :sector, presence: true
validates :experience_req, presence: true
validates :job_info, presence: true
end
def comment_params
should be
def request_params

ActiveModel::MissingAttributeError in RequestsController#create, can't write unknown attribute `request_id'

I'm getting an error regarding saving a request for a job on a website I'm making. Basically the user (candidate) will make a request for a job through the job/show.html.erb page, the comment will then display on the show.html.erb page with any other candidates who have also applied for the job. When the user types their name in the text box and submits it I get the error mentioned above. After looking online it seems the problem lies in my realtionships in the Models. Any ideas?
RequestsController
class RequestsController < ApplicationController
before_action :authorise
#set_request, only: [:show, :edit, :update, :destroy]
def create
#job = Job.find params[:job_id]
#request = #job.requests.new(request_params) <- Error highlights this line
#request.candidate_id = #current_candidate.id #sets the user_id FK
#request.save #saves the #comment
# object to the comments table
respond_to do |format|
format.html{redirect_to #job}
end
end
private
def request_params
#This is the method ehich whitelists the data fields from the format
params.require(:request).permit(:content, :job_id, :candidate_id)
end
end
Request Model
class Request < ActiveRecord::Base
belongs_to :job, dependent: :destroy
has_many :candidates
end
Candidate Model
class Candidate < ActiveRecord::Base
has_secure_password
validates_uniqueness_of:can_email
belongs_to :request
validates :can_name, presence: true
validates :can_surname, presence: true
validates :college, presence: true
validates :can_email, presence: true
validates :address, presence: true
validates :experience, presence: true
validates :password_digest, presence: true
validates :college_year, numericality: { only_integer: true }
end
Job Model
class Job < ActiveRecord::Base
belongs_to :sector
has_many :requests, dependent: :destroy
validates :name, presence: true
validates :employer, presence: true
validates :sector, presence: true
validates :experience_req, presence: true
validates :job_info, presence: true
end
assuming that in your routes.rb file you have the following routes defined:
resources :jobs do
resources :requests
end
which nests the routes, (see http://guides.rubyonrails.org/routing.html#nested-resources)
It is completely unnecessary to pass in job_id in, via the requests part of the params. i.e. You don't need to include it as an input in your form because the url already passes the param in.
look at your server output the params hitting your 'create` action should look something like this:
params = { job_id: 1, request: {content: "hello world", candidate_id: "123"}}
in that case you permit the following:
def request_params
params.require(:request).permit(:content, :candidate_id)
end
and the first two lines of create will be correct:
#job = Job.find(params[:job_id])
#request = #job.requests.new(request_params)

Upgrading attr_accessible from Rails 3 to Rails 4

I'm trying to systematically upgrade from rails 3 to rails 4 and all of my 25 models are based on attr_accessor! So before getting into that can anyone provide me a simple example on how to do this. I've read the documentation and other topics but it's not clear on how to do it since this is my first upgrade Rodeo.
class Settings < ActiveRecord::Base
image_accessor :favicon
attr_accessible :company_name, :show_hot_jobs, :show_students, :subheading, :show_testimonials, :show_on_boarding, :max_concurrent_applications
attr_accessible :image_uid, :max_concurrent_application_groups
attr_accessible :primary_color, :white_color, :gray_color, :opacity, :locale, :lang_nl, :lang_fr, :lang_de, :lang_en, :privacy_page
attr_accessible :show_evp, :show_contact_person, :show_jobs_for_you
attr_accessible :favicon, :favicon_uid, :remove_favicon, :retained_favicon
attr_accessible :home_url, :show_correspondence, :show_appointment
attr_accessible :sliderone_uid, :slidertwo_uid, :sliderthree_uid, :sliderfour_uid, :sliderfive_uid
attr_accessible :sliderone_link, :slidertwo_link, :sliderthree_link, :sliderfour_link, :sliderfive_link
attr_accessible :sliderone_testoverview, :slidertwo_testoverview, :sliderthree_testoverview, :sliderfour_testoverview, :sliderfive_testoverview
attr_accessible :sliderone_page, :slidertwo_page, :sliderthree_page, :sliderfour_page, :sliderfive_page
validate :any_lang_present?
validates :max_concurrent_applications, :numericality => { :greater_than_equal_to => 1 }
validates :max_concurrent_application_groups, :numericality => { :greater_than_equal_to => 1 }
# Fav Icon Validation
validates_property :ext, of: :favicon, :in => ['ico', 'png', 'gif']
has_paper_trail
has_many :setting_translations, :foreign_key => :setting_id
accepts_nested_attributes_for :setting_translations, :allow_destroy => true, :reject_if => :all_blank
attr_accessible :setting_translations_attributes, :allow_destroy => true
translates :subheading, :company_name, :image_uid, :home_url, :sliderone_uid, :slidertwo_uid, :sliderthree_uid, :sliderfour_uid, :sliderfive_uid
translates :sliderone_link, :slidertwo_link, :sliderthree_link, :sliderfour_link, :sliderfive_link
translates :sliderone_testoverview, :slidertwo_testoverview, :sliderthree_testoverview, :sliderfour_testoverview, :sliderfive_testoverview
translates :sliderone_page, :slidertwo_page, :sliderthree_page, :sliderfour_page, :sliderfive_page
attr_accessible can be converted like so:
From
class Settings
attr_accessible :home_url
accepts_nested_attributes_for :setting_translations
end
class SettingTranslation
attr_accessible :etc
end
To
class SettingsController
def create
#settings = Settings.new(settings_params)
# ...
end
private
def settings_params
params.require(:settings).permit(
:home_url,
:setting_translations_attributes => [:id, :_destroy, :etc]
)
end
end
Note, you have to include :_destroy if you want to allow destroy on that model (:allow_destroy => true), and you have to include all attributes that should be accessible from any nested attributes. Though you remove attr_accessible when you've permitted, you do not remove accepts_nested_attributes_for.
Just remove attr_accessible from model. and add permit params according to need in controller.
like below :
class SupportTicketsController < ApplicationController
def create
#support_ticket = SupportTicket.create(house_params)
......
end
private
def house_params
params.require(:support_ticket).permit(:subject, :message, ....)
end
end
and if you don't want to make this much changes then add "protected_attributes" gem https://github.com/rails/protected_attributes in your gemfile And everything would work as before.

Rails Nested Resource Routes

I'm fairly new to rails and I don't think I'm understanding the routing completely. When I try to access the edit action I get the following error:
ActiveRecord::RecordNotFound in StoreController#show
Couldn't find Gear with id=edit
Rails.root: /Users/dave/rails_projects/outdoor
Application Trace | Framework Trace | Full Trace
app/controllers/store_controller.rb:7:in `show'
Request
Parameters:
{"user_id"=>"104",
"id"=>"edit"}
Show session dump
Show env dump
Response
Headers:
None
Here is my view with the link that is throwing this error:
<li><%= link_to "Store Appearance", edit_user_store_path(#user) %></li>
Here is my nested route:
resources :users do
resources :store
end
Here is my controller
class StoreController < ApplicationController
def index
#store = current_user.gears.paginate(page: params[:page])
end
def show
#gears = Gear.find(params[:id]).user.gears.paginate(page: params[:page])
end
def edit
end
def update
end
end
Model Store
class Store < ActiveRecord::Base
attr_accessible :storeimage, :storename
belongs_to :user
validates :user_id, :presence => true
end
Model User
class User < ActiveRecord::Base
attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :userimage, :remove_userimage
has_secure_password
has_many :gears
has_many :comments, :dependent => :destroy
has_one :store, :dependent => :destroy
before_save :create_remember_token
require 'carrierwave/orm/activerecord'
mount_uploader :userimage, UserpicUploader
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :first_name, presence: true,
length: {:maximum => 50 }
validates :last_name, presence: true,
length: {:maximum => 50 }
validates :email, presence: true,
format: {:with => email_regex},
uniqueness: {:case_sensitive => false}
validates :password, presence: true,
confirmation: true,
length: {within: 6..40}
include Tire::Model::Search
include Tire::Model::Callbacks
def name
first_name + " " + last_name
end
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Please help.
You need to pass both the user_id and id params in the URL when you're accessing a store object nested under a user, so your URL should look like this:
/users/1/stores/3/edit
Versus:
/users/1/stores/edit
You also need to pass both of those as arguments to your path helper, ie:
edit_user_store_path(#user, #store)

Resources