在视图中延迟加载关联时引发异常

时间:2011-05-05 09:27:50

标签: ruby-on-rails ruby

使用熟悉的Rails示例关联,其中帖子有很多评论:

控制器

...
@posts = Post.find(:all)
...

查看

...
<% @posts.comments.each do |comment| %>
...

评论关联在视图中加载延迟。我想确保在渲染视图之前,所有数据库查询都在控制器中进行。对于这个例子来说,这不是什么大问题,但它应该能够更容易地在一个更复杂的例子中发现SQL N + 1查询。

我想看到的控制器代码是:

...
@posts = Post.find(:all, :include => :comments)
...

一旦我们开始渲染视图,有没有办法阻止延迟加载关联?我希望有一种方法可以在关联缺失时引发异常,但仅限于一旦我们在视野中。

是否有任何插件可以执行此操作?

1 个答案:

答案 0 :(得分:1)

这是一个几乎可以实现我想要的黑客:

内部config / initializers / prevent_lazy_loading_in_view.rb

class LazyLoadingPreventedInViewException < ActionView::TemplateError
  def initialize template_error
    super(template_error.instance_eval{@template}, template_error.instance_eval{@assigns}, template_error.original_exception)
  end
end
class ActionController::Base
  def render_with_lazy_load_prevention *args, &block
    ActiveRecord::Base.connection.disconnect!
    begin
      render_without_lazy_load_prevention *args, &block
    rescue ActionView::TemplateError => e
      if e.message['not connected']
        raise LazyLoadingPreventedInViewException.new(e)
      else
        raise e
      end
    end
    ActiveRecord::Base.connection.reconnect!
  end
  alias_method_chain :render, :lazy_load_prevention
end

这将在渲染视图时断开数据库连接。任何延迟加载尝试都将导致包含“未连接”的消息的异常。我们拦截了这个异常并给它一个新名称“LazyLoadingPreventedInViewException”,只是为了让它稍微不那么神秘。

这绝对是 hack ,也不是很好。可能会给毫无防备的开发人员造成一些很大的困惑。如果我决定保留它,我当然不会继续生产。