From 80e240b94de66ba96eaba5c7b0dc7a3a809ae0d5 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Mon, 21 Jun 2021 10:55:43 -0500 Subject: [PATCH] Fix: duplicate objects stored in has many association after save Fixes https://github.com/rails/rails/issues/42549 --- .../associations/collection_association.rb | 6 +++--- .../cases/associations/belongs_to_associations_test.rb | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 04e18a43a5582..d901986e6e8cb 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -79,7 +79,7 @@ def ids_writer(ids) def reset super @target = [] - @replaced_targets = Set.new + @replaced_or_added_targets = Set.new @association_ids = nil end @@ -440,7 +440,7 @@ def concat_records(records, raise = false) end def replace_on_target(record, skip_callbacks, replace:, inversing: false) - if replace && (!record.new_record? || @replaced_targets.include?(record)) + if replace && (!record.new_record? || @replaced_or_added_targets.include?(record)) index = @target.index(record) end @@ -454,7 +454,7 @@ def replace_on_target(record, skip_callbacks, replace:, inversing: false) yield(record) if block_given? - @replaced_targets << record if inversing || index + @replaced_or_added_targets << record if inversing || index || record.new_record? if index target[index] = record diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 95772da202797..2d7afd897a460 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -1471,6 +1471,16 @@ def test_multiple_counter_cache_with_after_create_update end end end + + test "assigning an association doesn't result in duplicate objects" do + post = Post.create!(title: "title", body: "body") + post.comments = [post.comments.build(body: "body")] + post.save! + + assert_equal 1, post.comments.size + assert_equal 1, Comment.where(post_id: post.id).count + assert_equal post.id, Comment.last.post.id + end end class BelongsToWithForeignKeyTest < ActiveRecord::TestCase