Raise exception when associations are lazy-loaded in the view
Using a familiar Rails example association where Posts have many Comments:
Controller
...
@posts = Post.find(:all)
...
View
...
<% @posts.comments.each do |comment| %>
...
The comments association gets lazy-loaded while in the view. I would like to ensure that all database queries happen in the controller before we get to rendering the view. It's n开发者_StackOverflowot a big deal for this example, but it should make it easier to spot SQL N+1 queries in a more complicated example.
The controller code I would like to see is this:
...
@posts = Post.find(:all, :include => :comments)
...
Is there a way to prevent lazy-loading associations once we start rendering the view? I'm hoping there is a way to cause it to throw an exception when an association is missing, but only once we're in the view.
Are there any plug-ins that do this?
This is a hack that almost does what I want:
Inside 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
This will disconnect the database while rendering the view. Any lazy-load attempts will cause an exception with message containing "not connected". We intercept this exception and give it a new name "LazyLoadingPreventedInViewException" just to make it slightly less cryptic.
This is definitely a hack, and not very good one either. Could cause some great confusion for the unsuspecting developer. If I decide to keep it, I certainly wont keep it on in production.
精彩评论