Skip to content

Commit

Permalink
Don't save through records twice
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sgrif committed Jun 17, 2014
1 parent e7a8fda commit 068f092
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 3 deletions.
8 changes: 8 additions & 0 deletions 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*
Expand Down
5 changes: 2 additions & 3 deletions activerecord/lib/active_record/autosave_association.rb
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Expand Up @@ -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

0 comments on commit 068f092

Please sign in to comment.