Вопрос

Я сталкиваюсь с некоторым поведением Rails 2.3.5 ActiveRecord, которое я не понимаю.Похоже, что идентификаторы ассоциации объекта могут обновляться непоследовательными способами.

Лучше всего это объясняется на примере:

Создать Post модель с атрибутом string 'title' и a Comment модель с атрибутом string 'content'.

Вот такие ассоциации:

class Post < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :post
end

Сценарий №1:В следующем коде я создаю один Post с ассоциированным Comment, создать второй Post Автор: find'к первому добавьте второе 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 и до того , как добавление comment2 Для post1.

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 перед добавлением нового Comment Для post1 имеет ли какое-либо значение, с какими комментариями были связаны post2?

Это было полезно?

Решение

Это связано со способом кэширования запросов Active Record и способом обработки ассоциаций has_many .

Если только ассоциация не загружена с помощью параметра :include во время поиска.Rails не будет заполнять ассоциацию для найденных записей до тех пор, пока это не потребуется.Когда ассоциация необходима какая-то запоминание это делается для сокращения количества выполняемых 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]

Н.Б. post2.comment_ids внутренне определяется как post2.comments.map(&:id)

P.S.Мой ответ на этот вопрос может помочь вам понять, почему post2 обновляется, несмотря на то, что вы его не трогаете.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top