How do I force rails to not use a cached result for has_many through relations?
caching, has-many-through, ruby-on-rails
Solution
I did some more research into this issue. While using `clear_association_cache` was convenient enough, adding it after every operation that invalidated the cache did not feel DRY. I thought Rails should be able to keep track of this. Thankfully, there is a way!
I will use your example models: A (has many B, has many C through B), B (belongs to A, has many C), and C (belongs to B).
We will need to use the `touch: true` option for the `belongs_to` method. This method updates the `updated_at` attribute on the parent model, but more importantly it also triggers an `after_touch` callback. This callback allows to us to automatically clear the association cache for any instance of A whenever a related instance of B or C is modified, created, or destroyed.
First modify the `belongs_to` method calls for B and C, adding `touch:true`
class B < ActiveRecord::Base
belongs_to :a, touch: true
has_many :cs
end
class C < ActiveRecord::Base
belongs_to :b, touch: true
end
Then add an `after_touch` callback to A
class A < ActiveRecord::Base
has_many :bs
has_many :cs, through: :bs
after_touch :clear_association_cache
end
Now we can safely hack away, creating all sorts of methods that modify/create/destroy instances of B and C, and the instance of A that they belong to will automatically have its cache up to date without us having to remember to call `clear_association_cache` all over the place.
Depending on how you use model B, you may want to add an `after_touch` callback there as well.
Documentation for `belongs_to` options and ActiveRecord callbacks:
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-belongs_to
Hope this helps!
Newer Rails versions
As @stevenspiel commented this method was made private and then in newer Rails, it was even removed. So you can use either `#reset` or `#reload` the instance (probably you want to reset the association).
class A < ActiveRecord::Base
has_many :bs
has_many :cs, through: :bs
after_touch :reset_associations
private
def reset_associations
bs.reset
cs.reset
end
end
Problem
I have the following three models (massively simplified): ``` class A < ActiveRecord::Base has_many :bs has_many :cs, :through => :bs end class B < ActiveRecord::Base belongs_to :a has_many :cs end class C < ActiveRecord::Base belongs_to :b end ``` It seems that A.cs gets cached the first time it is used (per object) when I'd really rather it not. Here's a console session that highlights the problem (the fluff has been edited out) First, the way it should work ``` rails console 001 > b = B.create 002 > c = C.new 003 > c.b = b 004 > c.save 005 > a = A.create 006 > a.bs << b 007 > a.cs => [#<C id: 1, b_id: 1>] ``` This is indeed as you would expect. The a.cs is going nicely through the a.bs relation. And now for the caching infuriations ``` 008 > a2 = A.create 009 > a2.cs => [] 010 > a2.bs << b 011 > a2.cs => [] ``` So the first call to a2.cs (resulting in a db query) quite correctly returned no Cs. The second call, however, shows a distinct lack of Cs even though they jolly well should be there (no db queries occurred). And just to test my sanity is not to blame ``` 012 > A.find(a2.id).cs => [#<C id: 1, b_id: 1>] ``` Again, a db query was performed to get both the A record and the associated C's. So, back to the question: How do I force rails to not use the cached result? I could of course resign myself to doing this workaround (as shown in console step 12), but since that would result in an extra two queries when only one is necessary, I'd rather not.