Has_Many :Through with Counter_Cache

Rails 4: counter_cache in has_many :through association with dependent: :destroy

Well, dependent: :destroy will destroy the associated records, but it won't update the counter_cache, so you may have wrong count in counter_cache. Instead you can implement a callback that will destroy the associated records, and update your counter_cache.

class Calendar < ActiveRecord::Base

has_many :administrations
has_many :users, through: :administrations


before_destroy :delete_dependents

private
def delete_dependents
user_ids = self.user_ids
User.delete_all(:calendar_id => self.id)
user_ids.each do |u_id|
Calendar.reset_counters u_id, :users
end
end
end

And similarly, implement this for User model too

Rails counter_cache on has_many through

Short answer is no. You cannot use post.update(authors: []) and trigger callbacks.

counter_culture gem that you provided builds its functionality on top of rails callbacks execution, I pointed in that url code which does it.

post.update(authors: []) this code doesn't trigger ActiveRecord callbacks.

Neither this one post.authors.find(1).delete.

So in order to trigger your callbacks and update counter_cache column, you will need to write some function that will resolve authors and trigger destroy

Consider this

some_conditions = { id: [1,2,3] }

post.authors.where(some_conditions).each(&:destroy)

For further reading you refer to this well-described answer about ActiveRecord callbacks

ActiveRecord has_many :through duplicating counter caches on mass assignment

So far my research has told me this is probably a bug. Here are some github issues already filed for this problem:

https://github.com/rails/rails/issues/3903

https://github.com/rails/rails/issues/3085

Apparently there is an undocumented automatic counter cache caused by has_many :through relations. So if Teacher.has_many :students, :through => :classrooms then teacher.students << student collection assignments already look for and increment teacher.students_count if that column exists.

If you add Classroom.belongs_to :teacher, :counter_cache => :students_count then an additional callback is triggered when the Classroom model is created, and the column gets incremented twice.

Effective work around: rename the counter cache columns to something else. Student#teacherz_count and Teacher#studentz_count were effective at allowing my test case to pass.

https://github.com/carlzulauf/test_app/commit/707a33f948d5d55a8aa942e825841fdd8a7e7705

I haven't yet been able to find where the problem lies in the ActiveRecord code base, so I won't accept my own answer for a while in case someone knows why has_many :through works this way and where the offending code lives.

Update

I believe I found the offending line of code. Commenting out this line resolves the problem:

https://github.com/rails/rails/blob/889e8bee82ea4f75adb6de5badad512d2c615b7f/activerecord/lib/active_record/associations/has_many_through_association.rb#L53

I can't seem to get edge rails up and running so I can't submit a pull for this bug yet. If someone else is able, please do.

Finding the offending line allowed me to craft a more effective monkey patch in my test app that resolves the issue without renaming any columns.

https://github.com/carlzulauf/test_app/commit/3c421b035bd032b91ff60e3d74b957651c37c7fa

Counter cache for a model with a many-to-many association

It seems like there is no real easy way to do this. If you look at this previous post. It seems like this comes up fairly often and sadly rails does not have an easy way to complete this task. What you will need to do is write some code like this.

Although, I would also suggest looking into has_and_belongs_to_many relationships as that might be what you have here.

A(Tag) has many C(posts) through B(tagging)

class C < ActiveRecord::Base
belongs_to :B

after_create :increment_A_counter_cache
after_destroy :decrement_A_counter_cache

private

def increment_A_counter_cache
A.increment_counter( 'c_count', self.B.A.id )
end

def decrement_A_counter_cache
A.decrement_counter( 'c_count', self.B.A.id )
end
end

counter_cache not decrementing for has_many associations in ActiveReord

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



Related Topics



Leave a reply



Submit