views:

467

answers:

4

I'm trying to add the user_id to a nested attribute that gets built by a parent controller but it doesn't seem to have the desired effect?

Ie. I have a model called Place.rb which accepts_nested_attributes_for :reviews and has_many :reviews, :as => :reviewable, :dependent => :destroy

The nested attribute works fine and I build it inside the Places controller like so...

new action

@review = @place.reviews.build(:user_id => current_user.id)

create action

params[:place].merge(:user_id => current_user.id)
params[:place][:reviews_attributes].merge!(:user_id => current_user.id)* bad
@place = Place.new(params[:place])

this is the original, for the place model to get a user_id, now i need the user_id for the nested reviews model as well. It might seem odd that places and reviews both have user_ids, but people can add new reviews for the same place...

possibly like this but doesn't work:

@place = Place.new(params[:place].merge(:user_id => current_user.id, :reviews_attributes => { :user_id => current_user.id } ))

get the error: undefined methodwith_indifferent_access' for 3:Fixnum`

or

@place = Place.new(params[:place].merge(:user_id => current_user.id, :reviews_attributes => { "0" =>  { :user_id => current_user.id }}))

which adds the correct user_id but replaces the content of the review with NULL ;-(

I was previously adding the user thru the form, but would like to do it thru the controller so that it only adds the user_id on creation, as a particular review might get updated by someone else and i don't want the update changing the user_id from the original writer...

old way which works:

<%= e.label :content, "Review" %><br />
<%= e.text_area :content, :rows => 20, :class => 'jquery_ckeditor' %><br />
<%= e.hidden_field :user_id, :value => current_user.id %> #want to remove this line

but thru the controller the build method with options has no effect? Any ideas? Can I not do this thru the build?

The output in log:

    Parameters: {"commit"=>"Submit", "action"=>"create", "city_id"=>"prague",
 "controller"=>"places", "place"=>{"address"=>"fsdfsdf", "name"=>"sdfsdfsd",
 "reviews_attributes"=>{"0"=>{"content"=>"<p>\r\n\tsdfsdfsdfsdfsdfsdfsdf sdfsdfsdf</p>\r
\n"}}, "website"=>"", "city_id"=>"1036", "place_type"=>"1"}}
+1  A: 

Assuming you have params[:review] as the attributes' hash, you need to do a merge!:

params[:review].merge!(:user_id => current_user.id)
@review = @place.reviews.build(params[:review])

Edit: I'm also assuming you'll use this is on create method.

Edit #2: It's not gonna work on new method because, as you can find at railsapi.com, the build method

"only works if an associated object already exists, not if it’s nil!"

Edit #3: I'm not sure if this is the best way, but I tested here and It worked...

You have this parameters:

{"commit"=>"Submit", "action"=>"create", "city_id"=>"prague",
   "controller"=>"places", "place"=>{"address"=>"fsdfsdf", "name"=>"sdfsdfsd",
   "reviews_attributes"=>{"0"=>{"content"=>"<p>\r\n\tsdfsdfsdfsdfsdfsdfsdf 
   sdfsdfsdf</p>\r\n"}}, "website"=>"", "city_id"=>"1036", "place_type"=>"1"}}

So you can access reviews' attributes this way: params[:place][:reviews_attributes] and, to merge the user_id attribute, you can do:

params[:place][:reviews_attributes].each_value {
   |v| v.merge!(:user_id => current_user.id) 
}

Now params[:place][:reviews_attributes] looks like this example:

{"0"=>{
   "user_id"=>"1",
   "content"=>"<p>\r\n\tsdfsdfsdfsdfsdfsdfsdf sdfsdfsdf</p>\r\n"
}}
j.
That's too ugly so i added the above to the question with specific new and create actions.
holden
it's never gonna work on `new` method. see my edited answer.
j.
i'm happy to do it on create, but i'm not sure how to merge the nested attribute... see above. :reviews_attributes => { :user_id => current_user.id } doesn't work.
holden
it's `params[:reviews_attributes].merge!(:user_id => current_user.id)`
j.
with that i get "undefined method `merge!' for nil:NilClass", tried params[:place][:reviews_attributes].merge!(:user_id => current_user.id), gets indifferent access error. added my log above.
holden
edited my answer...
j.
+1  A: 

Try this:

params[:place][:user_id] = current_user.id
params[:place][:reviews_attributes].each do |key, review|
  review[:user_id] = current_user.id
end if params[:place][:reviews_attributes]
KandadaBoggu
A: 

You may or may not want to add a hidden field in the form that includes the user_id. Then the params hash will already have the value. If you're worried about tampering, you could compare it to the current_user.id. Then again, maybe that's why you're trying it this way to begin with?

David Krider
A: 

Maybe you are using form_for with a poor syntax. I have quit using hidden_fields after looking at this: http://apidock.com/rails/ActionView/Helpers/FormHelper/form_for

and this comment: (God Bless You, nachocab!)

nachocab - November 2, 2008 params hash gets the model id automatically

The params hash gets automatically populated with the id of every model that gets passed to form_for. If we were creating a song inside an existing album:

URL:/albums/209/songs/new form_for [@album, @song] do |f| ... f.submit "Add" end

The params hash would be:

params = {"commit"=>"Add", "authenticity_token"=>"...", "album_id"=>"209", "song"=>{"song_attributes"=>{...}} }

So, in the songs_controller you could use this album_id in a before_filter:

before_filter :find_album
protected def find_album @album = Album.find(params[:album_id]) end

If you only did this:

form_for @song do |f|

You would get this params hash:

params = {"commit"=>"Add", "authenticity_token"=>"...", "song"=>{"song_attributes"=>{...}} }

FernandoFabreti