1

我遇到了一些的Rails 2.3.5 ActiveRecord的行为,我不明白。看起来一个对象可以以不一致的方式更新其关联ID。Rails的ActiveRecord关联不一致更新

这最好用一个例子说明:

创建Post模型与串属性'title'Comment模型与串属性'content'

这里有关联:

class Post < ActiveRecord::Base 
    has_many :comments 
end 

class Comment < ActiveRecord::Base 
    belongs_to :post 
end 

场景#1:在下面的代码创建一个Post与相关联的Comment,通过find创建第二Post“荷兰国际集团的第一,添加第二Comment到第一Post并发现第二Post有没有明确的分配与之相关的第二Comment

post1 = Post.new 
post1 = Post.new(:title => 'Post 1') 
comment1 = Comment.new(:content => 'content 1') 
post1.comments << comment1 
post1.save 
# Create a second Post object by find'ing the first 
post2 = Post.find_by_title('Post 1') 
# Add a new Comment to the first Post object 
comment2 = Comment.new(:content => 'content 2') 
post1.comments << comment2 
# Note that both Comments are associated with both Post objects even 
# though I never explicitly associated it with post2. 
post1.comment_ids # => [12, 13] 
post2.comment_ids # => [12, 13] 

场景#2:再次运行上述命令,但这次插入一个额外的命令,在它的表面上,不应该影响结果。额外的命令是post2.comments这发生在之后创建comment2之前添加comment2post1

post1 = Post.new 
post1 = Post.new(:title => 'Post 1A') 
comment1 = Comment.new(:content => 'content 1A') 
post1.comments << comment1 
post1.save 
# Create a second Post object by find'ing the first 
post2 = Post.find_by_title('Post 1A') 
# Add a new Comment to the first Post object 
comment2 = Comment.new(:content => 'content 2A') 
post2.comments # !! THIS IS THE EXTRA COMMAND !! 
post1.comments << comment2 
# Note that both Comments are associated with both Post objects even 
# though I never explicitly associated it with post2. 
post1.comment_ids # => [14, 15] 
post2.comment_ids # => [14] 

注意,只有一个post2在这种情况下,而在方案1相关的评论有两个。

大问题:为什么会运行post2.comments添加新Commentpost1之前对这些意见进行了与post2相关的有什么区别?

回答

2

这与该活动记录缓存请求和has_many关联的处理方式的方式做。

除非协会与eagerloaded:包括查找过程中选择。直到需要时,Rails才会为找到的记录填充关联。当需要关联时,会执行一些memoization以减少执行的SQL查询的数量。

通过在问题的代码步进:

post1 = Post.new(:title => 'Post 1') 
comment1 = Comment.new(:content => 'content 1') 
post1.comments << comment1 # updates post1's internal comments cache 
post1.save 

# Create a second Post object by find'ing the first 
post2 = Post.find_by_title('Post 1') 

# Add a new Comment to the first Post object 
comment2 = Comment.new(:content => 'content 2') 
post1.comments << comment2 # updates post1's internal comments cache 

# Note that both Comments are associated with both Post objects even 
# though I never explicitly associated it with post2. 
post1.comment_ids # => [12, 13] 

# this is the first time post2.comments are loaded. 
# SELECT comments.* FROM comments JOIN comments.post_id = posts.id WHERE posts.id = #{post2.id} 
post2.comment_ids # => [12, 13] 

方案2:

post1 = Post.new(:title => 'Post 1A') 
comment1 = Comment.new(:content => 'content 1A') 
post1.comments << comment1 
post1.save 

# Create a second Post object by find'ing the first 
post2 = Post.find_by_title('Post 1A') 

# Add a new Comment to the first Post object 
comment2 = Comment.new(:content => 'content 2A') 

# first time post2.comments are loaded. 
# SELECT comments.* FROM comments JOIN comments.post_id = posts.id WHERE 
# posts.id = post2.comments #=> Returns one comment (id = 14) 
# cached internally. 

post1.comments << comment2 
# Note that both Comments are associated with both Post objects even 
# though I never explicitly associated it with post2. 
post1.comment_ids # => [14, 15] 

# post2.comment has already been cached, so the SQL query is not executed again. 

post2.comment_ids # => [14] 

N.B. post2.comment_ids在内部定义为post2.comments.map(&:id)

P.S.我对this question的回答可能会帮助您理解为什么post2在您未触及的情况下会更新。

+0

感谢您的回答。但这种行为似乎不对?缓存应该提高性能,不会导致结果不一致。 – rlandster 2010-02-14 16:29:45

+0

这更多的是一个并发问题。 Rails不希望外部来源改变与模型实例相关的事物。在这种情况下,外部来源是指来自某些特定实例以外的任何操作。如果坚信这是错误的,请提交错误报告。 – EmFi 2010-02-14 19:15:24