Skip to content

Commit

Permalink
Prevent duplicate records when preloading has_many
Browse files Browse the repository at this point in the history
When preloading a has_many association, we were simply concatenating
the preloaded records without regard to whether they were already
loaded on the owner. Even though there is a check for `loaded?` in this
part of the preloader, some persisted records may not be marked as such.
For example, if a record is created via `owner.association.create`. This
change reverts to the previous behavior of replacing the target
association while also preserving non-persisted records, which was the
goal of #50129.

Co-authored-by: John Hawthorn <john@hawthorn.email>
  • Loading branch information
dustinbrownman and jhawthorn committed Dec 13, 2023
1 parent ee65e97 commit f5f48cb
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 2 deletions.
Expand Up @@ -247,8 +247,8 @@ def associate_records_to_owner(owner, records)
association = owner.association(reflection.name)

if reflection.collection?
association.loaded!
association.target.concat(records)
not_persisted_records = association.target.reject(&:persisted?)
association.target = records + not_persisted_records
else
association.target = records.first
end
Expand Down
11 changes: 11 additions & 0 deletions activerecord/test/cases/associations_test.rb
Expand Up @@ -804,6 +804,17 @@ def test_preload_makes_correct_number_of_queries_on_relation
end
end

def test_preload_does_not_concatenate_duplicate_records
post = posts(:welcome)
post.reload
post.comments.create!(body: "A new comment")

ActiveRecord::Associations::Preloader.new(records: [post], associations: :comments).call

assert_equal post.comments.length, post.comments.count
assert_equal post.comments.all.to_a, post.comments
end

def test_preload_for_hmt_with_conditions
post = posts(:welcome)
_normal_category = post.categories.create!(name: "Normal")
Expand Down

0 comments on commit f5f48cb

Please sign in to comment.