161

I've got a pretty simple question. But haven't found a solution so far.

So here's the JSON string I send to the server:

{
  "name" : "abc",
  "groundtruth" : {
    "type" : "Point",
    "coordinates" : [ 2.4, 6 ]
  }
}

Using the new permit method, I've got:

params.require(:measurement).permit(:name, :groundtruth)

This throws no errors, but the created database entry contains null instead of the groundtruth value.

If I just set:

params.require(:measurement).permit!

Everything get's saved as expected, but of course, this kills the security provided by strong parameters.

I've found solutions, how to permit arrays, but not a single example using nested objects. This must be possible somehow, since it should be a pretty common use case. So, how does it work?

Jared Beck
  • 16,796
  • 9
  • 72
  • 97
Benjamin M
  • 23,599
  • 32
  • 121
  • 201
  • have a look into this http://stackoverflow.com/questions/14483963/rails-4-0-strong-parameters-nested-attributes-with-a-key-that-points-to-a-hash – Rajarshi Das Aug 26 '13 at 05:17
  • 1
    @vinodadhikary It was correct… I think the OP is confused. As odd as it sound when you want to permit nested attributes you do specify the attributes of nested object within array. On the other hand if you want nested of multiple objects then you wrap it inside a hash… see http://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-permit and https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/strong_parameters.rb#L246-L247 – j03w Aug 26 '13 at 05:31

4 Answers4

200

As odd as it sound when you want to permit nested attributes you do specify the attributes of nested object within an array. In your case it would be

Update as suggested by @RafaelOliveira

params.require(:measurement)
      .permit(:name, :groundtruth => [:type, :coordinates => []])

On the other hand if you want nested of multiple objects then you wrap it inside a hash… like this

params.require(:foo).permit(:bar, {:baz => [:x, :y]})


Rails actually have pretty good documentation on this: http://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-permit

For further clarification, you could look at the implementation of permit and strong_parameters itself: https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/strong_parameters.rb#L246-L247

j03w
  • 3,679
  • 1
  • 21
  • 15
  • 8
    both cases are the same in this answer, actually, it's just that the curly brackets are optional around { :groundtruth => [...]}; It's a hash but the interpreter is able to determine where the hash begins and ends without explicit curly brackets. – speakingcode Mar 20 '14 at 20:25
  • Nested arrays of attributes do not allow nested attributes. Nested attributes and attr_accessor are listed in my application as "Unpermitted parameters". Still looking for safe solution. – Katarzyna Aug 19 '15 at 22:30
  • In case of multiple nested objects, you should also permit the id for this to work. More info here : http://stackoverflow.com/questions/18308714/nested-fields-and-strong-parameters – Fabrice Carrega May 13 '16 at 10:24
  • 1
    This only permits ONE set of nested attributes. This will not work in the case of a one to many. – RubyRedGrapefruit Mar 10 '18 at 00:54
  • @speakingcode if that's the case I suspect it's better practice to keep the curly brackets so the reader of the code has a clearer understanding of what's going on – stevec Aug 23 '20 at 05:05
25

I found this suggestion useful in my case:

  def product_params
    params.require(:product).permit(:name).tap do |whitelisted|
      whitelisted[:data] = params[:product][:data]
    end
  end

Check this link of Xavier's comment on github.

This approach whitelists the entire params[:measurement][:groundtruth] object.

Using the original questions attributes:

  def product_params
    params.require(:measurement).permit(:name, :groundtruth).tap do |whitelisted|
      whitelisted[:groundtruth] = params[:measurement][:groundtruth]
    end
  end
sealocal
  • 10,897
  • 3
  • 37
  • 50
M.ElSaka
  • 1,264
  • 13
  • 20
  • 4
    Just a side note, This will still show in the log as unpermitted parameters but the model will accept them anyways. – Weston Ganger Jan 25 '16 at 01:49
  • 5
    Not sure of Rails 4 but in my Rails 5 project I have to call `permit!` to be whitelisted or else it's remained unpermitted after tapping it. In this case it would be `params[:measurement][:groundtruth].permit!` – nayiaw Feb 22 '17 at 07:54
  • @nayiaw i also get the unpermitted message but adding `permit!` raises this error `NoMethodError (undefined method `permit!' for #):` – wuliwong Apr 17 '18 at 22:17
  • @wuliwong `permit!` method is not available in `Array`. You'll need to have access to the respective class instance to have access to `permit!` (it's been a while so I've forgotten the class name but it's something like `ActionController::Parameters` based on [this page](http://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-permit)). – nayiaw Apr 18 '18 at 10:12
12

Permitting a nested object :

params.permit( {:school => [:id , :name]}, 
               {:student => [:id, 
                            :name, 
                            :address, 
                            :city]},
                {:records => [:marks, :subject]})
CJBrew
  • 2,720
  • 1
  • 20
  • 27
Codiee
  • 3,047
  • 2
  • 17
  • 18
3

If it is Rails 5, because of new hash notation: params.permit(:name, groundtruth: [:type, coordinates:[]]) will work fine.