How to cleanly update a list of HABTM associations in Rails 4?
Asked Answered
B

1

8

I'm trying to update a User record with the list of associated Addresses, connected through the has and belongs to many association.

The request body I'm sending through javascript is:

{"id":10,"name":"John Smith", "address_ids":[4,8]}

This is what I'm getting in the Rails server log:

Started PUT "/api/users/10" for 127.0.0.1 at 2013-10-03 16:30:43 +0200

Processing by Api::UsersController#update as HTML

Parameters: {"id"=>"10", "name"=>"John Smith", "address_ids"=>[4, 8], "user"=>{"id"=>"10", "name"=>"John Smith"}}

The thing to notice above is the address_ids array is not making it into the user hash according to the log.

In the controller i'm using strong parameters and updating the User like so:

attributes = params.require(:user).permit(:id, :name, {:address_ids => []}) 
@user.update_attributes(attributes)

The problem is the attributes don't contain the address_ids at the point of update so the associated records are not getting updated.

Temporary Solution

I've worked around the issue manually assigning the address_ids key to the attributes after they come back from the strong parameters check like so:

attributes = params.require(:user).permit(:id, :name, {:address_ids => []})
if params.has_key?(:address_ids)   
  attributes[:address_ids] = params[:address_ids] 
end
@user.update_attributes(attributes)

This works fine but doesn't seem to me this is how it is supposed to work? Why are the adderss_ids not getting auto assigned? How can this be implemented in a clear way?

Bitterling answered 3/10, 2013 at 15:13 Comment(0)
M
9

If you want the addresses saved with the User you need to ensure your client side code sends the address_ids parameters as follows:

Parameters: {"user"=>{"id"=>"10", "name"=>"John Smith", "address_ids"=>["4", "8"]}}

The input field name attribute will be something like:

<input name="user[address_ids][]">

I also noticed you seem to have your user attributes repeated in your parameters so you may want to carefully review how your client code is generating the request.

Mystic answered 4/10, 2013 at 12:53 Comment(3)
Thanks. To sum up, the solution is to make sure the object sent from javascript has the root key with the name of the resource. Still not clear why some of the fields get assigned correctly even without it and some not...Bitterling
Is there any way to do this by passing Address UUIDs rather than expose the sequential IDs?Tribade
@John. I'm definitely in favor of UUIDs for a whole lot of reasons, but in this case whether the ids are UUIDs or sequential integers doesn't make a difference to the approach here. Whatever IDs are passed, they come from the database regardless of the ID scheme used.Mystic

© 2022 - 2024 — McMap. All rights reserved.