Apparently in my Rails application ActiveRecord is running more SQL queries than I would like. I have a simple one-to-many relation. For each package…
class Package < ActiveRecord::Base
has_many :screenshots
end
…there are several screenshots…
class Screenshot < ActiveRecord::Base
belongs_to :package
def image_url
"/screenshots/#{self.package.name}/#{self.id}_#{size}.png"
end
end
My first simple task is to show an index page with packages and screenshots. For each package I want to show its screenshots. The path to a screenshot image is determined by the package name. Let's do an example and fetch the package record for the "3dchess" package:
pkg = Package.includes(:screenshots).find_by_name('3dchess')
As you can see I already eager-load the screenshots. ActiveRecord runs these two SQL queries:
Package Load (1.4ms) SELECT "packages".* FROM "packages" WHERE "packages"."name" = '3dchess' LIMIT 1
Screenshot Load (2.1ms) SELECT "screenshots".* FROM "screenshots" WHERE "screenshots"."package_id" IN (243)
I had expected that this could be done in one query but I don't mind the two. But when I'm displaying 20 screenshots on an index page I suddenly get 20 additional SQL queries. And it turns out that they are caused by the virtual attribute "image_url" of the Screenshot model.
What I'm doing in my template:
<% @packages.each do |package| %>
<li>
<% if package.screenshots %>
<%= image_tag package.screenshots.first.image_url %>
<% end %>
</li>
<% end %>
And each call of "image_tag package.screenshots.first.image_url" runs a SQL query like:
SELECT "packages".* FROM "packages" WHERE "packages"."id" = $1 ORDER BY "packages"."id" ASC LIMIT 1
So although I'm eagerloading the screenshots when I'm loading a package - it does not work the other way round.
Or to put it another way - this creates the two SQL queries mentioned above:
pkg = Package.includes(:screenshots).find_by_name('3dchess')
But when I try to access
pkg.package
then ActiveRecord runs an additional query getting the "package" information although it should already have them.
So my question is: how can I make ActiveRecord eager-load the backreference from "screenshot" to "package" automatically?