My Rails 3 app has 2 models and a third that's join table between them and their has_many relationships. Basically, User and Show are joined by SavedShow, allowing users to save a list of shows:
class Show < ActiveRecord::Base
has_many :saved_shows
has_many :users, :through => :saved_shows
end
class User < ActiveRecord::Base
has_many :saved_shows
has_many :shows, :through => :saved_shows
end
class SavedShow < ActiveRecord::Base
belongs_to :user, :counter_cache => :saved_shows_count
belongs_to :show
end
I've noticed that the counter_cache field (shows_saved_count) gets incremented automatically just fine, but not decremented. The core of the issue seems to be that removing shows from a user's list is done via delete, which does not trigger updating of the counter_cache:
current_user.shows.delete(@show)
However, I can't call the destroy method here, since that not only deleted the User/Show association in SavedShow, but also the Show object itself, which is not what I want.
Is a counter_cache in this kind of scenario not an appropriate use?
There appears to be a discussion about this as a bug back in 2009, and fixes were discussed, but I'm still seeing the issue in the latest Rails 3.0.
I would just write my own custom handling in the model, but there seems to be no after_delete callback that I can hook into (presumably this is the reason decrementing doesn't work in the first place). Right now, there's only one place in my own code where a delete of the association could occur, so I'll just manually make a call to update the counter, but this seems like like such a fundamental shortcoming or bug of ActiceRecord associations with counter_cache, that I'm wondering if I'm not just missing something.
If this is indeed a genuine problem with counter_caches, what would be the best workaround?
Faced a related issue in Rails 5 (with self referential counter cache through a join table) and fixed it as below:
class User < ActiveRecord::Base
has_many :saved_shows, :counter_cache => :saved_shows_count
has_many :shows, :through => :saved_shows
end
https://guides.rubyonrails.org/association_basics.html#options-for-has-many-counter-cache
[RAILS 6]
On a standard has_many through relation :
class Parent < ApplicationRecord
has_many :joins,
foreign_key: :parent_id,
dependent: :destroy,
counter_cache: :joins_count
has_many :children, through: :joins, source: 'child'
...
class Join < ApplicationRecord
belongs_to :parent, counter_cache: :joins_count
belongs_to :child
end
The counter cache has to be specified on both sides, otherwise it won't we decremented on relation deletion
Same issues here but on Rails 2.3. Worth noticing that also adding a touch, like:
belongs_to :user, :counter_cache => :saved_shows_count, :touch => true
Won't update counter cache nor the related updated_at field on association.delete(object).
To workaround the issue usually we manipulate the join model, but that also have some drawbacks.
Patch is here: https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/2824-patch-has_many-through-doesnt-update-counter_cache-on-join-model-correctly#ticket-2824-18
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With