I'm having an issue with mass assignment for nested attributes. I'm having a hash in params that represents an object that results from a form_for form.
I tried to authorize the params like this but I get the following error...
ActiveModel::ForbiddenAttributesError
for
params.require(:country).permit(:language, :flag_path)
#country.update(params[:country])
Here is the params array :
{"utf8"=>"✓",
"authenticity_token"=>"xxxxxxx",
"country"=>{"language"=>"xxxx",
"flag_path"=>"xxxxx"},
"commit"=>"Update",
"country_id"=>"xxxx"}
Thanks for your help.
EDIT : I know it is possible to user permit! but if I understand correctly, this authorize all parameters for the ressource and I'd like to permit only some of it.
There are two problems in your code:
1st:
Consider if below is your permit param method in your controller :
def country_param
params.require(:country).permit(:language, :flag_path)
end
then your update action should be like this:
#country.update(country_param)
not
#country.update(params[:country])
2nd: Why you have country_id in your update action. It should be id instead.
It's not big one both will work. But with country_id You will not reach to proper edit action.
Anyway according to your params your action should be look like:
def update
#country = Country.find(params[:country_id])
#country.update(country_param)
end
private
def country_param
params.require(:country).permit(:language, :flag_path)
end
What you are doing with params.require(:country).permit is the correct way to do it. ActiveModel::ForbiddenAttributesError will be raised if any one of the params is not permitted so it is likely that you have missed off one of the params.
Check all of the params in your params hash and make sure that they have been permitted, it looks like commit and contry_id are missing, for example.
Related
In rails 4.x, strong_parameters require parameters to be explicitly permitted. Yet, in the following example, I do NOT get a ForbiddenAttributesError - why does :id not throw when in the show action even though it is not explicitly permitted?
def FooController
...
def show
#foo = Foo.find(params[:id]) # why no exception here?
end
private
def foo_params
params.require(:foo).permit(:name, :address) # note: No :id here
end
end
See: http://edgeguides.rubyonrails.org/action_controller_overview.html#strong-parameters
"With strong parameters, Action Controller parameters are forbidden to be used in Active Model mass assignments until they have been whitelisted."
Doing a find is completely valid, and is, in fact, shown in the example in the documentation linked to, above.
Strong parameters are used only for assignment of attributes. You can freely search and perform other operations with any param, just not mass assignment.
You can see more in-depth explanation and examples in Rails Guides
For Rails, params[:id] outside from default params.
Query string:
www.example.com/foo/123?bar=1&baz=2
Request path:
www.example.com/foo/123 where 123 is params[:id]
Paramerts:
bar=1&baz=2 this can be permitted
If you pass 123 to parameters then you need permitted :id.
There is no need of explicitly permitting the :id unless you want to.Rails will do it implicitly.If want to check whether the :id is whitelisted or not,you can do puts params[:foo] after it is created or you can just see the log.you will see something like this
{id=>some_id, "name"=>"some_name", "adddress"=>"some_address"}
So,defining a Foo object like this
#foo = Foo.find(params[:id])
will not throw an exception.
Hope it helped!
As title says, why does Rails prefer to use the #params variable inside of a Controller action when you are responding to the action instead of passing the individual parameters through the function arguments when we call the function?
Other frameworks use this (i.e, ASP MVC) and I was just wondering if there was a reason for that design decision, because it doesn't seem very intuitive.
Ie. Why does Rails do
def index
name = params[:name]
end
Instead of
def index(name)
end
The point is, most of the actions in a controller handles the view REST-fully. The params comes from the user's browser when they interact with the page or send a new variable request to the page.
These requests are variable, and Rails makes it uniform by maintaining the parameters in params hash. If the following GET requests arrive:
http://localhost:3000/products?color=red&quality=best
the params hash will automatically be populated as {'color' => 'red', 'quality' => 'best'}. Rails doesn't expect your action to manually handle the parameters.
Similarly, consider you are getting a POST request from a page where a user filled a form. In that scenario, the params obtain the parameters which are composed with form helpers inside views.
Though in hyptothetical case you are dealing with general methods instead of actions, such as below, you will have to do it by passing arguments.
def show
if params['color'] == 'red'
#product = obtain_product('red')
else
#,..
end
end
def obtain_product(color)
Product.where('color = ?', color).first
end
Hope it is clear. :)
#kidorrails has a great answer, and I wanted to add to it:
If you wanted to pass the params to each method directly, it would go against the #1 Rails convention - keep it DRY. By having a separate params hash, you not only have access to all the params you want, but you can access them through as many methods as you need
For example, take strong_params:
#controller
def new
#model = Model.new
end
def create
#model = Model.new(strong_params)
#model.save
end
private
def strong_params
params.require(:model).permit(:your, :params)
end
As #apneadiving mentioned, the params hash is created in another part of the stack, meaning it's available over all the methods required. It's most efficient & versatile way to do it IMO
For rails 4 Strong Parameters I need to access two of the fields. How can I do that?
def branch_params
params.require(:branch).permit( :equal_number, :equal_main_branch_number,
:history, :inquiry_email, :internal_notes,
:is_main_branch, :main_branch_number, :name,
:number,:region_id, :serving )
end
I understand this part. Strong Parameters
def create
#branch = Branch.new(branch_params)
end
Now I need to pass two of the fields to pass into a method.
format_branch_number(:equal_number, :equal_main_branch_number)
According to docs
Action Controller parameters are forbidden to be used in Active Model
mass assignments until they have been whitelisted
what means, you cant use them to create AR object, but you can still use your params to do some stuff with them, so you can simply format_branch_number(params[:equal_number], params[:equal_main_branch_number])
try this:
format_branch_number(params[:branch][:equal_number], params[:branch][:equal_main_branch_number])
I am trying to pass a simple variable in my params to a class method, however it doesnt seem to work. This seems elementary, but I'm still learning. Can someone explain why this doesn't work and offer an alternative? My code is below.
controller
#profile = current_user.profile
#dogs = Dog.by_profile(params[#profile])
model
def self.by_profile(profile)
Dog.where(kids_under_10: profile.kids_under_10 )
end
*note: profile.kids_under_10 is a boolean. When I manually replace it with true or false, everything works fine.
params is a special rails hash that contains url parameters. So your code is looking for a url parameter passed with the request containing the string version of your user profile. This is definitely not what you want to be doing.
When you're calling a rails model method, you call it with arguments like any other method: Dog.by_profile(#profile)
You don't want the params part, or you're trying to do something crazy that should be refactored :)
Your key value for params should look like params[:profile].
So try #dogs = Dog.by_profile(params[:profile]).
Because params is a hash that comes from a request. What you are doing is trying to search the hash with an instance variable which is wrong.
I think what you meant is to do params[:profile] or just #profile
I am using Rails v2.3.2.
If :
params[:car]={"name"=>"mycar", "brand"=>"toyota"}
I tried to udpate a #car instance by:
#car.update_attributes(params[:car])
but the #car is not updated.
Why I can not update like this? Do I must update the #car by specify each filed like following:
#car.update_attributes(:name=>params[:car][:name], :brand=>params[:car][:brand])
instead of update with the params[:car] as a whole like:
#car.update_attributes(params[:car])
Anyone can explain to me?
P.S. the params is:
{"commit"=>"Save", "authenticity_token"=>"w/d2uI/2tK9vSZvtF9oQDjY5iBPL8fji33IZcpm9cY0=", "_method"=>"put", "action"=>"update", "id"=>"4", "controller"=>"cars", "car"=>{"name"=>"mycar", "brand"=>"toyota"}
No, never update an object like this:
#car.update_attributes(:name=>params[:car][:name], :brand=>params[:car][:brand])
This is wrong. This would only be useful if you only want to update these attributes.
Always use this instead:
#car.update_attributes(params[:car])
Assuming your params don't have anything more in them besides name and brand then these two statements are identical.
What you're doing in the first one is that you're building this hash:
{ :name => "mycar", :brand => "Toyota" }
And in the second one, you're passing through a hash that is basically identical, with the only difference being the object's id.
As for why the object is not saving, try calling update_attributes and then call .errors on the object after that and that will return any validation errors that were encountered when saving.