Nested attributes unpermitted parameters
Solution 1
Seems there is a change in handling of attribute protection and now you must whitelist params in the controller (instead of attr_accessible in the model) because the former optional gem strong_parameters became part of the Rails Core.
This should look something like this:
class PeopleController < ActionController::Base
def create
Person.create(person_params)
end
private
def person_params
params.require(:person).permit(:name, :age)
end
end
So params.require(:model).permit(:fields)
would be used
and for nested attributes something like
params.require(:person).permit(:name, :age, pets_attributes: [:id, :name, :category])
Some more details can be found in the Ruby edge API docs and strong_parameters on github or here
Solution 2
From the docs
To whitelist an entire hash of parameters, the permit! method can be used
params.require(:log_entry).permit!
Nested attributes are in the form of a hash. In my app, I have a Question.rb model accept nested attributes for an Answer.rb model (where the user creates answer choices for a question he creates). In the questions_controller, I do this
def question_params
params.require(:question).permit!
end
Everything in the question hash is permitted, including the nested answer attributes. This also works if the nested attributes are in the form of an array.
Having said that, I wonder if there's a security concern with this approach because it basically permits anything that's inside the hash without specifying exactly what it is, which seems contrary to the purpose of strong parameters.
Solution 3
or you can simply use
def question_params
params.require(:question).permit(team_ids: [])
end
Solution 4
Actually there is a way to just white-list all nested parameters.
params.require(:widget).permit(:name, :description).tap do |whitelisted|
whitelisted[:position] = params[:widget][:position]
whitelisted[:properties] = params[:widget][:properties]
end
This method has advantage over other solutions. It allows to permit deep-nested parameters.
While other solutions like:
params.require(:person).permit(:name, :age, pets_attributes: [:id, :name, :category])
Don't.
Source:
https://github.com/rails/rails/issues/9454#issuecomment-14167664
Solution 5
Today I came across this same issue, whilst working on rails 4, I was able to get it working by structuring my fields_for as:
<%= f.select :tag_ids, Tag.all.collect {|t| [t.name, t.id]}, {}, :multiple => true %>
Then in my controller I have my strong params as:
private
def post_params
params.require(:post).permit(:id, :title, :content, :publish, tag_ids: [])
end
All works!
jcanipar
Updated on April 25, 2020Comments
-
jcanipar about 4 years
I have a
Bill
object, which has manyDue
objects. TheDue
object also belongs to aPerson
. I want a form that can create theBill
and its childrenDues
all in one page. I am trying to create a form using nested attributes, similar to ones in this Railscast.Relevant code is listed below:
due.rb
class Due < ActiveRecord::Base belongs_to :person belongs_to :bill end
bill.rb
class Bill < ActiveRecord::Base has_many :dues, :dependent => :destroy accepts_nested_attributes_for :dues, :allow_destroy => true end
bills_controller.rb
# GET /bills/new def new @bill = Bill.new 3.times { @bill.dues.build } end
bills/_form.html.erb
<%= form_for(@bill) do |f| %> <div class="field"> <%= f.label :company %><br /> <%= f.text_field :company %> </div> <div class="field"> <%= f.label :month %><br /> <%= f.text_field :month %> </div> <div class="field"> <%= f.label :year %><br /> <%= f.number_field :year %> </div> <div class="actions"> <%= f.submit %> </div> <%= f.fields_for :dues do |builder| %> <%= render 'due_fields', :f => builder %> <% end %> <% end %>
bills/_due_fields.html.erb
<div> <%= f.label :amount, "Amount" %> <%= f.text_field :amount %> <br> <%= f.label :person_id, "Renter" %> <%= f.text_field :person_id %> </div>
UPDATE to bills_controller.rb This works!
def bill_params params .require(:bill) .permit(:company, :month, :year, dues_attributes: [:amount, :person_id]) end
The proper fields are rendered on the page (albeit without a dropdown for
Person
yet) and submit is successful. However, none of the children dues are saved to the database, and an error is thrown in the server log:Unpermitted parameters: dues_attributes
Just before the error, the log displays this:
Started POST "/bills" for 127.0.0.1 at 2013-04-10 00:16:37 -0700 Processing by BillsController#create as HTML<br> Parameters: {"utf8"=>"✓", "authenticity_token"=>"ipxBOLOjx68fwvfmsMG3FecV/q/hPqUHsluBCPN2BeU=", "bill"=>{"company"=>"Comcast", "month"=>"April ", "year"=>"2013", "dues_attributes"=>{ "0"=>{"amount"=>"30", "person_id"=>"1"}, "1"=>{"amount"=>"30", "person_id"=>"2"}, "2"=>{"amount"=>"30", "person_id"=>"3"}}}, "commit"=>"Create Bill"}
Has there been some change in Rails 4?
-
jcanipar about 11 yearsI have changed my BillController to look like this:
def bill_params params.require(:bill).permit(:company, :month, :year, :dues_attributes[:amount, :person_id]) end
I am now getting this error: no implicit conversion of Symbol into Integer -
jcanipar about 11 yearsWell, it helps to put the colon in the right place... This is exactly what needed to be done. Thanks @thorsten-muller !
-
Arcolye almost 11 yearsDON'T FORGET THE ID!!!!
pets_attributes: [:id, :name, :category]
Otherwise, when you edit, each pet will get created again. -
Bartłomiej Skwira over 10 yearsAwesome, I cound't get to permit a range parameter explicitly, this save me some hours.
-
8bithero over 10 yearsYeah, using .permit! is usually seen as a potential security concern. You'd only really want to use it if the user is an admin, but even then I'd be wary of its use.
-
andorov over 10 yearsYou need to do
Person.create(person_params)
or it won't call the method. Instead you'll getActiveModel::ForbiddenAttributesError
. -
Clifton Labrum over 10 yearsMy nested attributes are in an array, too. Is
.permit!
the only option? I can't get it to work even with all the model's attributes permitted because it chokes on the array. -
Pathogen over 9 yearsAlso, if you want to destroy items from the form, you also need to whitelist the hidden
:_destroy
parameter. i.e.pets_attributes: [:id, :name, :category, :_destroy]
-
Hugo almost 9 yearsIf you keep getting an error, make sure to add it at the end of the line (not in the middle). WRONG: :aaa, pets_attributes: [], :bbb. GOOD: :aaa, :bbb, pets_attributes: []
-
Chris Cirefice over 8 years@Arcolye I literally just dealt with this issue and I was very confused to see my records being duplicated after an UPDATE. What blows my mind is that Rails requires the
:id
, even in Rails 4, when this (in my opinion) should be an implicit parameter. Rails is great, but this to me doesn't make any damn sense. -
BenKoshy almost 8 yearswill this work if you are submitting multiple pet objects?
-
BenKoshy almost 8 yearshi thank you @KingsleyIjomah - what if you want to white list specific attributes of the children?