Ruby on Rails – Nested Object Forms using a :belongs_to association

In a project I am currently working on, I have a one-way relationship using a belongs_to association.  I wanted to use the newer Nested Object Forms functionality added in Rails 2.3 to allow me to modify the properties of the related object in one form.

For the purposes of this post I’m going to use the following simplified code:

#address.rb
class Address < ActiveRecord::Base
  attr_accesible :address_string
end

#user.rb
class User < ActiveRecord::Base
  attr_accessible :name

  belongs_to :address
  accepts_nested_attributes_for :address
end

I ran into an issue when I was testing out this functionality in script/console and trying to assign attributes to the address through the user. I would encounter the error “WARNING: Can’t mass-assign these protected attributes: address_attributes” in my development.log. I proceeded to try and google a solution to this problem, and found many posts from users which encountered this issue, but none of them provided a solution. It appeared many people simply abandoned attempting to use nested attributes with a :belongs_to association. The solution to this problem is actually quite simple (once you know it!). Here’s the console log and the database log from my initial attempt:

#script/console
>> user = User.first=> #<User id: 1, name: "Bill", address_id: 1, created_at: "2010-08-20 21:49:52", updated_at: "2010-08-20 22:06:37">
>> params = {:address_attributes => {:address_string => "Up the tree", :id => 1}}
=> {:address_attributes=>{:address_string=>"Up the tree", :id=>1}}
>> user.update_attributes(params)=> true
>> user.address=> #<Address id: 1, address_string: "down the street", created_at: "2010-08-20 21:49:53", updated_at: "2010-08-20 21:49:53">


#development.log     
WARNING: Can't mass-assign these protected attributes: address_attributes

Note that the console even returns true from the operation, but the update address attributes aren’t assigned and the string is still “down the street” instead of “Up the tree” that we were trying to assign. The solution to this problem is to make sure that address attributes are added to the attr_accesible in the user model:

#user.rb
class User < ActiveRecord::Base
  attr_accessible :name, :address_attributes
  belongs_to :address
  accepts_nested_attributes_for :address
end

Now the error will no longer occur, and the address will be succesfully updated, as in the following console log.

#script/console
>> user = User.first=> #<User id: 1, name: "Bill", address_id: 1, created_at: "2010-08-20 21:49:52", updated_at: "2010-08-20 22:06:37">
>> params = {:address_attributes => {:address_string => "Up the tree", :id => 1}}
=> {:address_attributes=>{:address_string=>"Up the tree", :id=>1}}
>> user.update_attributes(params)                                               => true
>> user.address=> #<Address id: 1, address_string: "Up the tree", created_at: "2010-08-20 21:49:53", updated_at: "2010-08-20 22:41:46">

I hope this post can help save some time for others who run across this same issue.

Happy Coding!

Tagged , , , , , , ,