提交 66679c8e 编写于 作者: I Ian Young

Update other counter caches on destroy

上级 34c7e73c
## Rails 4.0.0 (unreleased) ##
* Models with multiple counter cache associations now update correctly on destroy.
See #7706.
*Ian Young*
* If inverse_of is true on an association, then when one calls +find()+ on
the association, ActiveRecord will first look through the in-memory objects
in the association for a particular id. Then, it will go to the DB if it
......
......@@ -31,7 +31,7 @@ def belongs_to_counter_cache_after_create_for_#{name}
end
def belongs_to_counter_cache_before_destroy_for_#{name}
unless marked_for_destruction?
unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == #{foreign_key.to_sym.inspect}
record = #{name}
record.class.decrement_counter(:#{cache_column}, record.id) unless record.nil?
end
......
......@@ -22,7 +22,7 @@ def handle_dependency
else
if options[:dependent] == :destroy
# No point in executing the counter update since we're going to destroy the parent anyway
load_target.each(&:mark_for_destruction)
load_target.each { |t| t.destroyed_by_association = reflection }
destroy_all
else
delete_all
......
......@@ -212,6 +212,7 @@ def add_autosave_association_callbacks(reflection)
# Reloads the attributes of the object as usual and clears <tt>marked_for_destruction</tt> flag.
def reload(options = nil)
@marked_for_destruction = false
@destroyed_by_association = nil
super
end
......@@ -231,6 +232,19 @@ def marked_for_destruction?
@marked_for_destruction
end
# Records the association that is being destroyed and destroying this
# record in the process.
def destroyed_by_association=(reflection)
@destroyed_by_association = reflection
end
# Returns the association for the parent being destroyed.
#
# Used to avoid updating the counter cache unnecessarily.
def destroyed_by_association
@destroyed_by_association
end
# Returns whether or not this record has been changed in any way (including whether
# any of its nested autosave associations are likewise changed)
def changed_for_autosave?
......
......@@ -427,6 +427,7 @@ def init_internals
@readonly = false
@destroyed = false
@marked_for_destruction = false
@destroyed_by_association = nil
@new_record = true
@txn = nil
@_start_transaction_state = {}
......
......@@ -115,6 +115,14 @@ class ::SpecialReply < ::Reply
end
end
test "update other counters on parent destroy" do
david, joanna = dog_lovers(:david, :joanna)
assert_difference 'joanna.reload.dogs_count', -1 do
david.destroy
end
end
test "reset the right counter if two have the same foreign key" do
michael = people(:michael)
assert_nothing_raised(ActiveRecord::StatementInvalid) do
......
......@@ -2,3 +2,6 @@ david:
id: 1
bred_dogs_count: 0
trained_dogs_count: 1
joanna:
id: 2
dogs_count: 1
sophie:
id: 1
trainer_id: 1
dog_lover_id: 2
class Dog < ActiveRecord::Base
belongs_to :breeder, :class_name => "DogLover", :counter_cache => :bred_dogs_count
belongs_to :trainer, :class_name => "DogLover", :counter_cache => :trained_dogs_count
belongs_to :breeder, class_name: "DogLover", counter_cache: :bred_dogs_count
belongs_to :trainer, class_name: "DogLover", counter_cache: :trained_dogs_count
belongs_to :doglover, foreign_key: :dog_lover_id, class_name: "DogLover", counter_cache: true
end
class DogLover < ActiveRecord::Base
has_many :trained_dogs, :class_name => "Dog", :foreign_key => :trainer_id
has_many :bred_dogs, :class_name => "Dog", :foreign_key => :breeder_id
has_many :trained_dogs, class_name: "Dog", foreign_key: :trainer_id, dependent: :destroy
has_many :bred_dogs, class_name: "Dog", foreign_key: :breeder_id
has_many :dogs
end
......@@ -230,14 +230,16 @@ def create_table(*args, &block)
t.integer :access_level, :default => 1
end
create_table :dog_lovers, :force => true do |t|
t.integer :trained_dogs_count, :default => 0
t.integer :bred_dogs_count, :default => 0
create_table :dog_lovers, force: true do |t|
t.integer :trained_dogs_count, default: 0
t.integer :bred_dogs_count, default: 0
t.integer :dogs_count, default: 0
end
create_table :dogs, :force => true do |t|
t.integer :trainer_id
t.integer :breeder_id
t.integer :dog_lover_id
end
create_table :edges, :force => true, :id => false do |t|
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册