强制加载模型实例上的关联

时间:2014-10-16 23:39:38

标签: ruby-on-rails activerecord

考虑一下:

class Post < ActiveRecord::Base
  has_many :comments

  def last_comment
    comments.last
  end
end

railsconsole> p = Post.last
  Post Load (0.2ms)  SELECT  `posts`.* FROM `posts`  WHERE ORDER BY `posts`.`id` DESC LIMIT 1
railsconsole> p.last_message
  Post Load (0.4ms)  SELECT `comments`.* FROM `comments`  WHERE `messages`.`post_id` = 14
railsconsole> p.last_message
  Post Load (0.4ms)  SELECT `comments`.* FROM `comments`  WHERE `messages`.`post_id` = 14

您会认为此处只应发生2个查询:初始查找,然后加载关联。应缓存对该关联的后续调用。但是,他们不是。永远不会加载和缓存该关联,因为Rails正在尝试智能并且只加载最后一条记录。由于Rails没有跟踪最后一条记录(或关联的第一个或任何其他自定义查询),它只是每次都返回答案。

但是,如果要缓存关联怎么办?我搜索了SO并找不到直接答案。

2 个答案:

答案 0 :(得分:5)

当您在ActiveRecord实例上调用关联时,您将返回一个代理对象,如果是has_many对象,则会获得CollectionProxy。

奇怪的是,您可以在集合代理上调用#load,但这不会加载关联缓存。

隐藏在Rails文档中:http://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-load_target

#load_target

|

class Post < ActiveRecord::Base
  has_many :comments

  def last_comment
    comments.load_target unless comments.loaded?
    comments.last
  end
end

希望其他人觉得这很有用。

答案 1 :(得分:2)

我会将last_comment设为关联

has_one :last_comment, -> { order 'created_at DESC' }, class_name: 'Comment'

这样你也可以包括,加入或者eager_load。