ActiveRecord似乎并不理解,给定具有嵌套属性的现有记录的一组参数,它可以创建一个新的嵌套记录,该记录本身具有嵌套的现有记录。 (关系树:Existing -> New -> Existing
)
这是一个错误,还是我错过了什么?
让我向您展示一个简单的例子。
以下是我的模特:
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
我在每个表格中都创建了一条记录,并相应地将它们相关联,因此每个表格中都有一条记录id=1
- 这是已知的。现在,如果我有一个现有用户,一个新帖子和一个现有组,并尝试使用accepts_nested_attributes_for
更新该记录,它就不喜欢它了:
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 >
它认为无法找到与新帖子相关的群组(具有已知ID)。
当我从group_attributes
中删除ID时,它会起作用(但它会创建一个新的组记录)。
当我向posts_attributes
提供ID时,它会起作用,并从group_attributes
中删除ID(并再次创建一个新组)。
它们都有ID时也可以使用。
这种关系正在发挥作用:
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
}
]
如果所有记录都是新的,那么在posts_attributes
创建期间使用group_attributes
和User
时,它也会完全有效。
它还不能在第一个例子中起作用吗? ActiveRecord应该足够聪明,可以解决这个问题......!
答案 0 :(得分:4)
以下是我认为正在发生的事情:您正在传递一个组的ID,向ActiveRecord指示该组存在。 ActiveRecord正在尝试使用 group_attributes 中的其他数据来查找该组。由于您正在 post_attributes 中执行此操作,因此ActiveRecord正在尝试通过帖子之间的关联找到该组和组。也就是说,ActiveRecord首先查找关联组 - 其中id = post.group_id - 然后从该结果中查找ID = 1的那个。对于父关系,这可能看起来有点奇怪和笨拙,就像你的情况一样,但是我确信你可以看到这是另一个方向的有用行为,其中嵌套属性代表一个或多个潜在的多个孩子。
但是,根据post_attributes中的数据创建的post对象尚未与组关联 - post.group_id为nil。因此,当ActiveRecord执行首次搜索以获取关联组时,它将显示为空。相应地,它没有在(空)结果中找到ID = 1的组。从技术上讲,记录就在那里,但在与帖子的关联方面并不存在。
您可以通过包含group_id =&gt;来证明这一点。 1在post_attributes中。我相信如果你这样做,ActiveRecord会通过关联找到该组,然后在结果中成功地从结果中选择ID = 1的组,然后用group_attributes中的附加数据更新该组。
另请注意,唯一原因在帖子中包含群组的嵌套属性就像您正在允许用户在创建的同时更新群组名称一样一篇新帖子。如果您要做的只是将新帖子链接到现有组,那么您只需要在post_attributes中包含group_id,就可以摆脱group_attributes。