5

I am working on a Model with an atter_accessor object named element. I want to pass the Array of form data to the element object. In Rails console I am getting Unpermitted parameter error.

Parameters: {"authenticity_token"=>"[FILTERED]", "category"=>{"name"=>"asfd", "body"=>"asf", "element"=>{"1"=>"asfd:text", "2"=>"asfd:text", "3"=>"asfd:text"}}, "type"=>"text", "commit"=>"Create Category"}
Unpermitted parameter: :element. Context: { controller: CategoriesController, action: create, request: #<ActionDispatch::Request:0x0000000106b3ff68>, params: {"authenticity_token"=>"[FILTERED]", "category"=>{"name"=>"asfd", "body"=>"asf", "element"=>{"1"=>"asfd:text", "2"=>"asfd:text", "3"=>"asfd:text"}}, "type"=>"text", "commit"=>"Create Category", "controller"=>"categories", "action"=>"create"} }

In model attr_accessor :elements

In controller

def category_params
  params.require(:category).permit(:name, :body, :elements => []) 
end

I tried with many alternatives changing the :elements to element: [] too, nothing worked. I think I am missing something here which is the reason I am getting an unpermitted parameters.

Shaunak
  • 17,377
  • 5
  • 53
  • 84
Santosh Aryal
  • 1,276
  • 1
  • 18
  • 41

2 Answers2

2

You haven't mentioned the version of rails you are using but, :elements => [] does not work because elements is a ruby hash and not an array

on rails 5.1+ you can use

params.require(:category).permit(:name, :body, :elements => {}) 
Shaunak
  • 17,377
  • 5
  • 53
  • 84
  • Great, I was missing this. Thanks – Santosh Aryal Feb 19 '22 at 05:51
  • 1
    This isn't quite correct. Nested parameters are actually instances of `ActionController::Parameters` and parameters whitelisting isn't some some case of an equity check. – max Feb 19 '22 at 16:19
  • @max I never claimed it was an equality check. Just showing the OP how to fix their problem. – Shaunak Aug 09 '22 at 03:37
  • Is it possible to specify the permitted hash keys inside `elements`? – Pioz May 04 '23 at 13:04
  • is it safe todo? since its accept whatever hash the elements have? – buncis May 10 '23 at 09:11
  • @max what do you mean by that max? – buncis May 10 '23 at 09:13
  • @Pioz yes it's possible to have nested objects in the strong params. Check out this answer https://stackoverflow.com/questions/18436741/rails-strong-parameters-nested-objects – Shaunak May 11 '23 at 02:53
  • @buncis generally speaking it's not a good idea, but it can depend on the application and the context. Sometimes when incoming json structure is dynamic there's almost no choice but to allow all keys. If possible you should avoid a blanket {} – Shaunak May 11 '23 at 02:57
0

A lot of confusion going on here besides the element / elements naming issue - pick one and stick with it.

If you want to pass an array as FormData in Rack applications you need to use keys with empty brackets:

irb(main):001:0> str = "elements[]=a&elements[]=b&elements[]=c"                                                         => "elements[]=a&elements[]=b&elements[]=b"
irb(main):002:0> Rack::Utils.parse_nested_query(str)                                                                   
=> {"elements"=>["a", "b", "c"]}   

If you place any sort of value in the brackets it will be parsed as a hash instead:

irb(main):003:0> str = "elements[1]=a&elements[2]=b&elements[3]=c"
=> "elements[1]=a&elements[2]=b&elements[3]=b"
irb(main):004:0> Rack::Utils.parse_nested_query(str)
=> {"elements"=>{"1"=>"a", "2"=>"b", "3"=>"c"}}    

When whitelisting an empty array will permit an array of permitted scalar values:

irb(main):005:0> params = ActionController::Parameters.new(Rack::Utils.parse_nested_query("elements[]=a&elements[]=b&elements[]=b"))
=> #<ActionController::Parameters {"elements"=>["a", "b", "b"]} permitted: false>
irb(main):006:0> params.permit(elements: [])
=> #<ActionController::Parameters {"elements"=>["a", "b", "b"]} permitted: true> 

When whitelisting hashes you pass an array of symbols which represent the keys you want to permit:

irb(main):006:0> params = ActionController::Parameters.new(foo: { bar: 1, baz: 2, woo: 3 })
irb(main):007:0> params.permit(foo: [:bar, :baz])
=> #<ActionController::Parameters {"foo"=>#<ActionController::Parameters {"bar"=>1, "baz"=>2} permitted: true>} permitted: true>

You can also permit a hash with arbitrary keys by passing an empty hash:

irb(main):008:0> params.permit(foo: {})
=> #<ActionController::Parameters {"foo"=>#<ActionController::Parameters {"bar"=>1, "baz"=>2, "woo"=>3} permitted: true>} permitted: true>  

This is a somewhat dangerous operation and should be done with care.

max
  • 96,212
  • 14
  • 104
  • 165
  • it's kinda confusing, so in strong parameters the bracket doesn't mean the parameters comes in array/multiples, to make ruby/rails know that your parameters is array/multiple you need to add bracket with index into it more read [here](https://guides.rubyonrails.org/form_helpers.html#understanding-parameter-naming-conventions) – buncis May 10 '23 at 13:54