0

I've got a bit of a puzzler on for strong_parameters.

I'm posting a large array of JSON to get processed and added as relational models to a central model. It looks something like this:

{
   "buncha_data": {
       "foo_data" [
           { "bar": 1, "baz": 3 },
           ...
       ]
   },
   ...
}

And I've got a require/permit flow that looks like it should work:

class TheController < ApplicationController
    def create
        mymodel = MyModel.create import_params
    end

    def import_params
        params.require(:different_property)
        params.require(:buncha_data).permit(foo_data: [:bar, :baz])
        params
    end
end

Yet in the create method when I iterate through this data to create the related model:

self.relatables = posted_data['buncha_data']['foo_data'].map do |raw|
    RelatedModel.new raw
end

I get a ActiveModel::ForbiddenAttributesError. What I've ended up having to do is iterate through the array on my own and call permit on each hash in the array, like so:

params.required(:buncha_data).each do |_, list|
    list.each{ |row| row.permit [:bar, :baz] }
end

What gives?

Mojowen
  • 437
  • 1
  • 4
  • 17
  • 1
    Are you accidentally using the copy of the parameters from before you've called `required` and `permit`? What makes me think that is, if you've set up a method to return the safe parameters where you've done `params.required(:buncha_data).permit...` then that params object will now begin at the `foo_data` level rather than where you've been doing `posted_data['buncha_data']['foo_data'].map...` – mikej Apr 15 '15 at 22:26
  • I've updated to show what my controller and parameter method looks like - not sure I'm totally following. I don't believe I'm using a copy – Mojowen Apr 15 '15 at 23:37
  • 1
    Am away from the laptop now but I'm almost certain `permit` returns a new copy of the parameters and doesn't update the existing object. So if you end your `import_params` method with the line that just reads `params` that is returning the original params object that `permit` hasn't been called on. – mikej Apr 15 '15 at 23:49
  • Ah that seems to be the trick - setting the params hash object to the required value. Which I've **never** seen the documentation - e.g. `params[:buncha_data] = params.require(:buncha_data).permit(foo_data: [:bar, :baz])` – Mojowen Apr 16 '15 at 00:29
  • @mikej Feel free to answer if you'd like, otherwise I'll add a summary of what I was doing wrong – Mojowen Apr 16 '15 at 15:12
  • 1
    your summary looks good. – mikej Apr 19 '15 at 10:49

1 Answers1

1

As MikeJ pointed out - require and permit do not update the object.

I rewrote my controller to be:

def import_params
    params[:different_property] = params.require(:different_property)
    params[:buncha_data] = params.require(:buncha_data).permit(foo_data: [:bar, :baz])
    params
end

And everything worked great. This is somewhat apparent if you read the source code.

Mojowen
  • 437
  • 1
  • 4
  • 17