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.
Related
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.
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 creating a instance variable that gets passed to my view. This variable 'post' has a user_id associated with it and I wanted to add an extra attribute called 'username' so I can also pass that and use it in the view.
Here is an example of what I would like to do.
#post = Post.find(params[:id])
#post.username = User.find(#post.user_id).username
A username column does exist on my Users model but not my Songs model. So it won't let me use
#post.username
I know I can just make an entirely new instance variable and put that information in there but I would like to keep everything nice and neat, in one variable. Which will also make my json rendered code look cleaner.
Any ideas on how I can accomplish this?
Thanks!
Based on the presence of a user_id in your Post model, you probably already have an association set up that can retrieve the username. It will probably save a lot of trouble to simply use the existing association:
#post = Post.find(params[:id])
username = #post.user.username
If you're likely to be querying more than one post at a time (e.g., on an index page, calling .includes to tell Rails to eager-load an association will help you avoid the N+1 problem:
#posts = Post.includes(:user).all
Finally, to include the associated record in your JSON output, pass the :include parameter as you serialize:
# in controller
render :json => #post.to_json(:include => :user)
This question includes a much more comprehensive discussion of serialization options. Well worth a read.
No need to pass a separate instance variable.
1. You can use #post.user.username in view itself.
2. Or you can create a helper and pass #post.user
def username user
user.username
end
I have a nested form that includes a lesson/questions/answers. The user populates answer fields and clicks submit. The hash is shown below:
Parameters: {"commit"=>"Submit Answers", "action"=>"update", "_method"=>"put", "authenticity_token"=>"y##########o=", "lesson"=>{"questions_attributes"=>{"0"=>{"id"=>"1", "answer"=>{"response"=>"answertextanswertext", "user_id"=>"2"}}, "1"=>{"id"=>"4", "answer"=>{"response"=>"answertextanswertext", "user_id"=>"2"}}}}, "id"=>"1", "controller"=>"lessons"}
In my update statement, I would like to loop through the answers and overwrite user_id for security purposes. I modified my update statement to the following:
def update
#lesson = Lesson.find(params[:id])
lesson_params = params[:lesson]
for q in lesson_params[:questions_attributes].values
for s in q.values
if !s[:user_id].nil?
s[:user_id] = current_user.id.to_s
end
end
end
if #lesson.update_attributes(lesson_params)
flash[:notice] = "Answers submitted successfully."
redirect_to lessons_path
else
render :action => 'edit'
end
end
I am a noob, so traversing the nested hash was a bit of trial and error. Is this the appropriate way to loop through the nested hash? Is this a good way to protect against mass-assignment?
Thanks, Alex
When you have nested data structures, looping over them is pretty much the only way to loop over them. Whether or not it is appropriate is another question. Realistically, this seems like a lot of work (even though it's pretty straightforward work) just to clean up your user ids. Honestly, it seems a lot easier to just modify the view that is passing these parameters in to set all those user ids to the currently-logged-in user before they get sent to your controller than looping over them (assuming that that is an appropriate solution to whatever security issue you are trying to solve).
Also, you may actually have some scoping issues with (for instance) q.values - this is an array that is equivalent to the values in the hash. Modifying s[:user_id] may not modify params[:lesson][:question_attributes][0][:user_id] (or whatever it works out to be).
As far as "protecting against mass-assignment" I am not sure why you are worried about that here, can you elaborate?
In one form I am creating a territory and editing multiple users. The "user_attributes" below are for the users and the "name" is for the territory. So for each user_attribute I wanted to update the user model.
params
{ "territory"=>{"name"=>"Central Canada",
"user_attributes"=>[{"user_id"=>"30"},{"user_id"=>"30"}]}
}
create action
#territory = #current_account.territories.new[:territory]
params[:user_attributes].each do |item|
#user = User.find(item[:user_id])
#user.update_attribute(:territory_id, #territory.id)
end
But rails is kicking back that params[:user_attributes] is nil. But you can see from the params its not. Am I missing something??
From what you posted, your user_attributes hash is INSIDE your territory hash. That should be your problem -- either move it outside, or do params[:territory][:user_attributes]
Try params["user_attributes"].