Skip to content

Instantly share code, notes, and snippets.

@coorasse
Last active February 10, 2023 02:16
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save coorasse/7b64571aab6352d972188f4710ecb274 to your computer and use it in GitHub Desktop.
Save coorasse/7b64571aab6352d972188f4710ecb274 to your computer and use it in GitHub Desktop.
Eager loading of association with parameter in Rails
begin
require 'bundler/inline'
rescue LoadError => e
warn 'Bundler version 1.10 or later is required. Please update your Bundler'
raise e
end
gemfile(true) do
source 'https://rubygems.org'
gem 'rails', '~> 5.2'
gem 'sqlite3'
end
require 'active_record'
require 'minitest/autorun'
require 'logger'
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
ActiveRecord::Base.logger = Logger.new(STDOUT)
# create your tables here
ActiveRecord::Schema.define do
create_table :products, force: true do |t|
t.string :name
end
create_table :users, force: true do |t|
t.string :name
end
create_table :custom_names, force: true do |t|
t.string :text
t.references :user
t.references :product
end
end
class Product < ActiveRecord::Base
has_many :custom_names, inverse_of: :product
end
class CustomName < ActiveRecord::Base
belongs_to :user
belongs_to :product, inverse_of: :custom_names
end
class User < ActiveRecord::Base
end
book, apple, orange = Product.create([{ name: 'book' }, { name: 'apple' }, { name: 'orange' }])
mario, luigi = User.create([{ name: 'Mario' }, { name: 'Luigi' }])
CustomName.create([{ user: mario, product: book, text: 'libro' },
{ user: luigi, product: book, text: 'buch' },
{ user: mario, product: apple, text: 'mela' }])
puts '=== N+1 QUERIES EXAMPLE ==='
Product.all.each do |product|
puts "product name: #{product.name}. mario names: #{product.custom_names.where(user: mario).map(&:text)}"
end
puts '=== JOIN EXAMPLE ==='
Product.joins(:custom_names).where(custom_names: { user: mario }).all.each do |product|
puts "product name: #{product.name}. mario names: #{product.custom_names.map(&:text)}"
end
puts '=== INCLUDES EXAMPLE ==='
Product.includes(:custom_names).where(custom_names: { user: mario }).all.each do |product|
puts "product name: #{product.name}. mario names: #{product.custom_names.map(&:text)}"
end
puts '=== GOOD EXAMPLE ==='
products = Product.all.to_a
ActiveRecord::Associations::Preloader.new.preload(products, :custom_names, CustomName.where(user: mario))
products.each do |product|
puts "product name: #{product.name}. mario names: #{product.custom_names.map(&:text)}"
end
@filipewl
Copy link

filipewl commented Nov 4, 2019

Hey Alessandro! Thanks for the article.

Just minor points that I had to go through to successfully run the script:

  1. Define the ApplicationRecord to fix eager_loading.rb:38:in ': uninitialized constant ApplicationRecord (NameError)`:
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end
  1. Comment out the line 75 (puts product.records_by_owner) because Product#records_by_owner isn't defined.

@coorasse
Copy link
Author

coorasse commented Nov 4, 2019

Thank you! I corrected it!

@mariusz-blaszczak
Copy link

This does not work with rails 6.0.

=== GOOD EXAMPLE ===
D, [2020-10-30T12:39:37.843010 #94979] DEBUG -- :   Product Load (0.0ms)  SELECT "products".* FROM "products"
D, [2020-10-30T12:39:37.845379 #94979] DEBUG -- :   CustomName Load (0.1ms)  SELECT "custom_names".* FROM "custom_names" WHERE "custom_names"."user_id" = ? AND "custom_names"."product_id" IN (?, ?, ?)  [["user_id", 1], ["product_id", 1], ["product_id", 2], ["product_id", 3]]
D, [2020-10-30T12:39:37.845798 #94979] DEBUG -- :   CustomName Load (0.0ms)  SELECT "custom_names".* FROM "custom_names" WHERE "custom_names"."product_id" = ?  [["product_id", 1]]
product name: book. mario names: ["libro", "buch"]
D, [2020-10-30T12:39:37.846143 #94979] DEBUG -- :   CustomName Load (0.0ms)  SELECT "custom_names".* FROM "custom_names" WHERE "custom_names"."product_id" = ?  [["product_id", 2]]
product name: apple. mario names: ["mela"]
D, [2020-10-30T12:39:37.846450 #94979] DEBUG -- :   CustomName Load (0.0ms)  SELECT "custom_names".* FROM "custom_names" WHERE "custom_names"."product_id" = ?  [["product_id", 3]]
product name: orange. mario names: []

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment