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
Convert Time from One Time Zone to Another in Rails
String Interpolation in Ruby Doesn't Work
To Use Self. or Not.. in Rails
Why Isn't Current Directory on My Ruby Path
Getting a "Bad Interpreter" Error When Using Brew
Difference Between Gemfile and Gemfile.Lock in Ruby on Rails
How to Update Ruby on Linux (Ubuntu)
Nokogiri Will Not Install - Error: Failed to Build Gem Native Extension
Ruby: How to Turn a Hash into Http Parameters
Installing Nokogiri on Osx 10.10 Yosemite
Imagemagick - "Core_Rl_Magick_.Dll Not Found" or How to Install Rmagick on Windows With Ruby 1.9.2
When Do I Need to Restart Server in Rails
Can't Install Pg Gem on Windows
Iterate Over Ruby Time Object With Delta
How to Understand the Difference Between Class_Eval() and Instance_Eval()