ActiveRecord does not seem to understand that, given the set of parameters for an existing record with nested attributes, it can create a new nested record, which itself has a nested existing record. (Relationship Tree: Existing -> New -> Existing )
Is this a mistake, or am I missing something?
Let me show you a simple example.
Here are my models:
class User < ActiveRecord::Base has_many :posts attr_accessible :name, :posts_attributes accepts_nested_attributes_for :posts end class Post < ActiveRecord::Base belongs_to :group belongs_to :user attr_accessible :content, :title, :group_attributes accepts_nested_attributes_for :group end class Group < ActiveRecord::Base has_many :posts attr_accessible :name end
I made one record in each table and linked them accordingly, so each table has a record in it with id=1 - this is known. Now, if I have an existing User, a new Mail, and an existing Group, and try updating this entry using accepts_nested_attributes_for , she doesn't like it:
1.9.3-p125 :044 > params { :id => 1, :name => "Billy", :posts_attributes => [ [0] { :title => "Title", :content => "Some magnificent content for you!", :group_attributes => { :id => 1, :name => "Group 1" } } ] } 1.9.3-p125 :045 > u #<User:0x00000002f7f380> { :id => 1, :name => "Billy", :created_at => Fri, 03 Aug 2012 20:21:37 UTC +00:00, :updated_at => Fri, 03 Aug 2012 20:21:37 UTC +00:00 } 1.9.3-p125 :046 > u.update_attributes params (0.1ms) begin transaction (0.1ms) rollback transaction ActiveRecord::RecordNotFound: Couldn't find Group with ID=1 for Post with ID= from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:462:in `raise_nested_attributes_record_not_found' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:332:in `assign_nested_attributes_for_one_to_one_association' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:288:in `group_attributes=' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:94:in `block in assign_attributes' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:93:in `each' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:93:in `assign_attributes' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/base.rb:498:in `initialize' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/reflection.rb:183:in `new' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/reflection.rb:183:in `build_association' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/associations/association.rb:233:in `build_record' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/associations/collection_association.rb:112:in `build' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:405:in `block in assign_nested_attributes_for_collection_association' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:400:in `each' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:400:in `assign_nested_attributes_for_collection_association' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:288:in `posts_attributes=' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:85:in `block in assign_attributes' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:78:in `each' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:78:in `assign_attributes' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/persistence.rb:216:in `block in update_attributes' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/transactions.rb:295:in `block in with_transaction_returning_status' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/connection_adapters/abstract/database_statements.rb:192:in `transaction' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/transactions.rb:208:in `transaction' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/transactions.rb:293:in `with_transaction_returning_status' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/persistence.rb:215:in `update_attributes' from (irb):15 from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.7/lib/rails/commands/console.rb:47:in `start' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.7/lib/rails/commands/console.rb:8:in `start' from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.7/lib/rails/commands.rb:41:in `<top (required)>' from script/rails:6:in `require' from script/rails:6:in `<main>'1.9.3-p125 :047 >
He thinks that he cannot find the group (with a known identifier) associated with the new Mail. It works when I remove the identifier from group_attributes (but it creates a new group entry). It works when I give posts_attributes id and remove id from group_attributes (and create a new group again). It also works when everyone has identifiers.
Relationships work:
1.9.3-p125 :059 > p = Post.new( { group_attributes: { name: 'Testing' } } ) #<Post:0x00000004212380> { :id => nil, :title => nil, :content => nil, :group_id => nil, :user_id => nil, :created_at => nil, :updated_at => nil } 1.9.3-p125 :060 > p.group [ [0] #<Group:0x00000004211868> { :id => nil, :name => "Testing", :created_at => nil, :updated_at => nil } ]
It also works fully when using posts_attributes and group_attributes during User creation, if all posts are new.
Should it work in the first example? ActiveRecord must be smart enough to figure it out ...!