Skip to content

Commit f5f48cb

Browse files
Prevent duplicate records when preloading has_many
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 rails#50129. Co-authored-by: John Hawthorn <[email protected]>
1 parent ee65e97 commit f5f48cb

File tree

2 files changed

+13
-2
lines changed

2 files changed

+13
-2
lines changed

activerecord/lib/active_record/associations/preloader/association.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,8 @@ def associate_records_to_owner(owner, records)
247247
association = owner.association(reflection.name)
248248

249249
if reflection.collection?
250-
association.loaded!
251-
association.target.concat(records)
250+
not_persisted_records = association.target.reject(&:persisted?)
251+
association.target = records + not_persisted_records
252252
else
253253
association.target = records.first
254254
end

activerecord/test/cases/associations_test.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,17 @@ def test_preload_makes_correct_number_of_queries_on_relation
804804
end
805805
end
806806

807+
def test_preload_does_not_concatenate_duplicate_records
808+
post = posts(:welcome)
809+
post.reload
810+
post.comments.create!(body: "A new comment")
811+
812+
ActiveRecord::Associations::Preloader.new(records: [post], associations: :comments).call
813+
814+
assert_equal post.comments.length, post.comments.count
815+
assert_equal post.comments.all.to_a, post.comments
816+
end
817+
807818
def test_preload_for_hmt_with_conditions
808819
post = posts(:welcome)
809820
_normal_category = post.categories.create!(name: "Normal")

0 commit comments

Comments
 (0)