2010-02-12 104 views
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