写Active_cord的has_many扩展是否重新查询数据库?

时间:2010-09-21 00:03:34

标签: ruby-on-rails optimization activerecord

如果我有这样的话:

class Post < ActiveRecord::Base
  has_many :comments, :as => :commentable do
    def approved
      find(:all, :conditions => {:approved => true})
    end
  end
end

class Comment < ActiveRecord::Base
  belongs_to :commentable, :polymorphic => true
end

...当我这样做时,我得到2次点击数据库(不包括找到帖子:p):

post = Post.first
post.comments #=> [Comment1, Comment2...]
post.comments.approved #=> [Comment1, Comment7...]

看起来它应该只是过滤内存中的当前注释数组,不是吗?它是这样做的吗?我问的原因是因为控制台在SELECT * FROM ...上显示post.comments.approved,即使我已调用post.comments。不应该在ActiveRecord中更好地优化它吗?

3 个答案:

答案 0 :(得分:1)

它是可选的,因为在某些情况下,您可能只想在需要时加载关联的对象。如果您希望将它们全部加载到内存中,则需要使用:include标志显式声明您希望包含在初始查询中的对象。例如:

post = Post.find(:first, :include => :comment)

您可能需要重写扩展程序以利用该功能......一种方法是更改​​“已批准”的函数以迭代附加到每个帖子的注释数组,并返回包含未批准注释的新数组过滤掉了。您已明确定义的“查找”将返回数据库。

答案 1 :(得分:1)

AR对关联扩展方法中的任何finder调用执行新查询。 您可以使用self来引用缓存的结果集。

  has_many :comments, :as => :commentable do
    def approved
      # uses the cached result set
      self.select{|c| c.approved == true}
    end
  end

答案 2 :(得分:0)

如果您的查询真的很简单,那么您想要的是命名范围:

class Comment
  named_scope :approved, :conditions => {:approved => true}
end

然后,你可以这样做:

@post.comments.approved.count #=> 1 DB hit!
@post.comments.count #=> Another DB hit - can't reuse same scope

查看#scope(Rails 2.3中的#named_scope)。