Home > other >  Rails Strong Params how to Permit a nested Array
Rails Strong Params how to Permit a nested Array

Time:06-29

I have the following params:

params={"data"=>
   {"type"=>"book", 
     "id"=>14, 
     "attributes"=> 
          {"id"=>14, 
              "created_at"=>"2022-06-27 21:15:39", 
              "title"=>"sdfdsf",   
              "targeting"=> { "release_times"=>[["4:00", "5:00"], ["5:00", "6:00"]], 
                              "days"=>["Monday", "Tuesday", "Wednesday"], 
                              "gender"=>["male", "female"]
                            }
           }
}

When I use this, I can get every value but release_times is always null:

When I use this:

safe_params = params.require(:data).permit( attributes: [:id, :created_at, :title, { targeting: {} }])

How can I extract the release times value? I tried doing this

safe_params = params.require(:data).permit( attributes: [:id, :created_at, :title, { targeting: [:days, :gender, release_times:[]]}])

But I get the error:

Validation failed: Targeting gender should be a list of values, Targeting days should be a list of values

How can I extract all the values from targeting including the release_times?

CodePudding user response:

As Ruby on Rails API states, when using ActionController::Parameters you want to declare that a parameter should be an array (list) by mapping it to a empty array. Like you did with release_times.

You should permit targeting params with [days: [], gender: []] instead of [:days, :gender]. This should solve the error.

But even them, release_times is an array of arrays, which I believe is not supported at the moment (there is an old issue for it).

One way you could bypass this would be by changing the way you're communicating release_times. Using an arrays of hashes instead of nested arrays.

From this:

"release_times"=>[["4:00", "5:00"], ["5:00", "6:00"]]

To this (or something similar):

"release_times"=>[{"start" => "4:00", "end"=>"5:00"}, {"start" =>"5:00", "end" => "6:00"}]

That way, you could do this:

safe_params = params.require(:data).permit(attributes: [:id, :created_at, :title, { targeting: [days: [], gender: [], release_times: [:start, :end]] }])

Exactly how you would implement that is up to you, but I hope it helps.

**Also, there was a typo with release_times.


You can do some testing yourself. Open rails c and do something like this:

param = ActionController::Parameters.new("targeting"=> { "release_times"=>[["4:00", "5:00"], ["5:00", "6:00"]]})
param.require(:targeting).permit(release_times: []) # > Doesn't return times.

new_param = ActionController::Parameters.new("targeting"=> { "release_times"=>[{"start" => "4:00", "end"=>"5:00"}, {"start" =>"5:00", "end" => "6:00"}] })
new_param.require(:targeting).permit(release_times: [:start, :end]) # > Return times.

Just an observation, using permit! would work. But as strong params doc says:

Extreme care should be taken when using permit! as it will allow all current and future model attributes to be mass-assigned.

So you could try to slice arguments yourself and them permit! - but I can't tell you that's the way to go.

Learn more about Mass Assignment Vulnerability here.

  • Related