Using Rails 3.1.3 and I’m trying to figure out why our counter caches aren’t being updated correctly when changing the parent record id via update_attributes.
class ExhibitorRegistration < ActiveRecord::Base
belongs_to :event, :counter_cache => true
end
class Event < ActiveRecord::Base
has_many :exhibitor_registrations, :dependent => :destroy
end
describe ExhibitorRegistration do
it 'correctly maintains the counter cache on events' do
event = Factory(:event)
other_event = Factory(:event)
registration = Factory(:exhibitor_registration, :event => event)
event.reload
event.exhibitor_registrations_count.should == 1
registration.update_attributes(:event_id => other_event.id)
event.reload
event.exhibitor_registrations_count.should == 0
other_event.reload
other_event.exhibitor_registrations_count.should == 1
end
end
This spec fails indicating that the counter cache on event is not being decremented.
1) ExhibitorRegistration correctly maintains the counter cache on events
Failure/Error: event.exhibitor_registrations_count.should == 0
expected: 0
got: 1 (using ==)
Should I even expect this to work or do I need to manually track the changes and update the counter myself?
From the fine manual:
There’s no mention of updating the cache when an object is moved from one owner to another. Of course, the Rails documentation is often incomplete so we’ll have to look at the source for confirmation. When you say
:counter_cache => true, you trigger a call to the privateadd_counter_cache_callbacksmethod andadd_counter_cache_callbacksdoes this:after_createcallback which callsincrement_counter.before_destroycallback which callsdecrement_counter.attr_readonlyto make the counter column readonly.I don’t think you’re expecting too much, you’re just expecting ActiveRecord to be more complete than it is.
All is not lost though, you can fill in the missing pieces yourself without too much effort. If you want to allow reparenting and have your counters updated, you can add a
before_savecallback to your ExhibitorRegistration that adjusts the counters itself, something like this (untested demo code):If you were adventurous, you could patch something like that into
ActiveRecord::Associations::Builder#add_counter_cache_callbacksand submit a patch. The behavior you’re expecting is reasonable and I think it would make sense for ActiveRecord to support it.