Skip to content

Instantly share code, notes, and snippets.

@VeerpalBrar
Last active December 21, 2021 16:55
Show Gist options
  • Save VeerpalBrar/2fc3ec1913cabadcaeaec44c96223a40 to your computer and use it in GitHub Desktop.
Save VeerpalBrar/2fc3ec1913cabadcaeaec44c96223a40 to your computer and use it in GitHub Desktop.
Fixing N+1 queries when using `validates_associated` with `has_many`
# Blog post: https://veerpalbrar.github.io/blog/2021/11/26/Fix-N+1-queries-when-using-validates_associated-with-has_many
### Setup
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
gem "rails"
gem "sqlite3"
end
require "active_record"
require "logger"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: "memory")
ActiveRecord::Base.logger = Logger.new(STDOUT)
Rails.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :authors, :force => true do |t|
t.string :name
t.integer :agent_id
end
create_table :books, :force => true do |t|
t.integer :author_id
t.string :title
end
create_table :covers, :force => true do |t|
t.integer :book_id
end
end
### Application Code
class Author < ActiveRecord::Base
has_many :books
validates_associated :books
# Solution 2: Preload Books and Covers before validation
# validate :books_are_valid
# def books_are_valid
# books.preload(:cover).all?(&:valid?)
# end
end
class Book < ActiveRecord::Base
belongs_to :author
has_one :cover
validates :title, presence: true
# solution 1: Only validate when neccessary
validates_associated :cover #, if: -> { title_changed? }
end
class Cover < ActiveRecord::Base
belongs_to :book
validates_presence_of :book
end
a = Author.new(name: "First Last")
Book.create!(author: a, title: "Title2")
Cover.create!(book: Book.last)
Cover.create!(book: Book.create!(author: a, title: "Title"))
Cover.create!(book: Book.create!(author: a, title: "Title3"))
a = Author.last
a.name = "New Name"
a.save!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment