Sonntag, 7. September 2014

Preload your ActiveRecord objects!

Eager loading associated ActiveRecord::Base model objects is always a good choice. A lot Ruby on Rails developer take advantage of the ActiveRecord::QueryMethods#includes and do not care anymore. A better approach is to care.
For example the original associations:
class Category < ActiveRecord::Base
  has_many :foods
end

class Food < ActiveRecord::Base
  belongs_to :category
end
and eager loading (aka preloading) the categories and their associated foods:
@categories = Category.includes(:foods).all
generates a SQL like:
SELECT "categories".* FROM "categories"
SELECT "foods".* FROM "foods" WHERE "foods"."category_id" IN (1, 2, 3)
Please note that ActiveRecord::QueryMethods#includes generates 2 SQL queries. The database tables categories and foods are not joined together.
And that is exactly the same what ActiveRecord::QueryMethods#preload does. That is why:
@categories = Category.preload(:foods).all
also generates the SQL:
SELECT "categories".* FROM "categories"
SELECT "foods".* FROM "foods" WHERE "foods"."category_id" IN (1, 2, 3)
But beware, the selection conditions are not allowed to refer to the preloaded association table attributes.
So something like:
@categories = Category.preload(:foods).where("foods.name LIKE '%corn%'").all
will raise an SQL exception:
SQLException: no such column: foods.name: SELECT "categories".* FROM "categories"  WHERE (foods.name LIKE '%corn%')
But as long as the query is simple and is intended to keep on being simple, it totally makes sense to prefer ActiveRecord::QueryMethods#preload to ActiveRecord::QueryMethods#includes.
Furthermore it is more intentional about what the code is doing. And that is always a win.
Further articles of interest:

Supported by Ruby 2.1.1 and Ruby on Rails 3.2.17