如何通过关系强制rails不使用has_many的缓存结果?

Oli*_*ver 11 caching ruby-on-rails has-many-through

我有以下三种模型(大规模简化):

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
Run Code Online (Sandbox Code Playgroud)

似乎A.cs在第一次使用时被缓存(每个对象),而我真的不喜欢它.

这是一个突出问题的控制台会话(已经删除了绒毛)

首先,它的工作方式

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>]
Run Code Online (Sandbox Code Playgroud)

这确实如你所料.a.cs很好地通过a.bs关系.

现在对于缓存的真气

008 > a2 = A.create
009 > a2.cs
=> []
010 > a2.bs << b
011 > a2.cs
=> []
Run Code Online (Sandbox Code Playgroud)

所以第一次调用a2.cs(导致数据库查询)非常正确地返回没有Cs.然而,第二次调用表明Cs明显不足,即使它们应该很好(不会发生数据库查询).

只是为了测试我的理智并不是罪魁祸首

012 > A.find(a2.id).cs
=> [#<C id: 1, b_id: 1>]
Run Code Online (Sandbox Code Playgroud)

同样,执行db查询以获取A记录和关联的C.

那么,回到问题:如何强制rails不使用缓存结果?我当然可以让自己做出这种解决方法(如控制台步骤12所示),但由于这只会导致额外的两个查询,所以我宁愿不这样做.

Dan*_*rip 16

我对这个问题做了一些更多的研究.尽管clear_association_cache使用方便,但在每次使缓存失效的操作之后添加它都不会感觉干燥.我认为Rails应该能够跟踪这一点.谢天谢地,有办法!

我将使用你的示例模型:A(有很多B,有很多C到B),B(属于A,有很多C)和C(属于B).

我们需要使用touch: truebelongs_to方法的选项.此方法更新updated_at父模型上的属性,但更重要的是它还会触发after_touch回调.只要修改,创建或销毁B或C的相关实例,此回调允许我们自动清除A的任何实例的关联缓存.

首先修改belongs_toB和C 的方法调用,添加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
Run Code Online (Sandbox Code Playgroud)

然后after_touch向A 添加回调

class A < ActiveRecord::Base
  has_many :bs
  has_many :cs, through: :bs

  after_touch :clear_association_cache
end
Run Code Online (Sandbox Code Playgroud)

现在我们可以安全地破解,创建各种修改/创建/销毁B和C实例的方法,并且它们所属的A实例将自动使其缓存更新而不必记住clear_association_cache全部调用这个地方.

根据您使用模型B的方式,您可能还想在其中添加after_touch回调.

belongs_to选项和ActiveRecord回调的文档:

http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-belongs_to

希望这可以帮助!

  • `clear_association_cache`在Rails 5中被私有化了https://github.com/rails/rails/commit/9d56958可能的替代方案包括`reload`并在关联上调用`reset` (2认同)