From 068f092ced8483e557725542dd919ab7c516e567 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Sat, 14 Jun 2014 09:21:31 -0600 Subject: [PATCH] Don't save through records twice If the through record gets created in an `after_create` hook that is defined before the association is defined (therefore after its `after_create` hook) get saved twice. This ensures that the through records are created only once, regardless of the order of the hooks. --- activerecord/CHANGELOG.md | 8 ++++++++ .../lib/active_record/autosave_association.rb | 5 ++--- .../has_many_through_associations_test.rb | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index fc726d6519943..efc10913f435c 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,11 @@ +* `has_many :through` associations will no longer save the through record + twice when added in an `after_create` callback defined before the + associations. + + Fixes #3798. + + *Sean Griffin* + * Add `bin/rake db:purge` task to empty the current database. *Yves Senn* diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index b3c3e26c9f6fc..dd92e29199651 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -184,9 +184,7 @@ def add_autosave_association_callbacks(reflection) before_save :before_save_collection_association define_non_cyclic_method(save_method) { save_collection_association(reflection) } - # Doesn't use after_save as that would save associations added in after_create/after_update twice - after_create save_method - after_update save_method + after_save save_method elsif reflection.has_one? define_method(save_method) { save_has_one_association(reflection) } unless method_defined?(save_method) # Configures two callbacks instead of a single after_save so that @@ -364,6 +362,7 @@ def save_collection_association(reflection) raise ActiveRecord::Rollback unless saved end + @new_record_before_save = false end # reconstruct the scope now that we know the owner's id diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 8641584c0cfb8..6895df73c1b24 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -1139,4 +1139,23 @@ def test_has_many_through_unscope_default_scope assert_equal 2, post.lazy_readers_unscope_skimmers.to_a.size assert_equal 2, post.lazy_people_unscope_skimmers.to_a.size end + + class ClubWithCallbacks < ActiveRecord::Base + self.table_name = 'clubs' + after_create :add_a_member + + has_many :memberships, inverse_of: :club, foreign_key: :club_id + has_many :members, through: :memberships + + def add_a_member + members << Member.last + end + end + + def test_has_many_with_callback_before_association + Member.create! + club = ClubWithCallbacks.create! + + assert_equal 1, club.reload.memberships.count + end end