Counter_Cache With Has_Many :Through

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

ROR: counter cache, has_many thougth, delete with nested params

I figured out a problem, all wrote in documentation:

This option can be used to configure a custom named :counter_cache. You only need this option when you customized the name of your :counter_cache on the belongs_to association.

In my case I must write next:

class Document < ActiveRecord::Base
has_many :sub_roles_documents, dependent: :destroy, counter_cache: :documents_count
has_many :sub_roles, through: :sub_roles_documents,class_name: '::SubRole'
end

Because I use the customize name for counter cache.



Related Topics



Leave a reply



Submit