Johannes Treitz
Johannes Treitz

Reputation: 51

Best practice for using scopes on already retrieved associations

class Foo < ActiveRecord::Base
  has_many :bars
end

class Bar < ActiveRecord::Base
  belongs_to :foo

  def self.activated
    where(activated: true)
  end
end

class FoosController < ApplicationController
  def index
    @foos = Foo.includes(:bars)
  end
end

Let's say I want to display a table of all Foos, including the number of activated bars on it. To prevent N+1 queries, I already include(s) :bars when retrieving all Foos. However, If I use foo.bars.activated, Rails fires up another query, not knowing about the already retrieved bars.

I see two options here:

  1. Instead of using a scope, filtering the already retrieved objects: foo.bars.select(&:activated)
  2. Defining another custom association, scoped to activated bars: has_many :activated_bars, -> { where(activated: true) }, class_name: 'Bar'

Are there other (better) ways to do this?

Upvotes: 1

Views: 53

Answers (1)

dre-hh
dre-hh

Reputation: 8044

The following will load all foo's with only activated bars and also eager_load the bars

 Foo.eager_load(:bars).merge(Bar.activated)

Updated:

Or you might also define a new scoped relationship on Foo and eager_load that

#Foo
has_many :activated_bars, ->{ activated }, clas_name: 'Bar'

#foos_controller#index
Foo.includes(:activated_bars)

See also http://blog.bigbinary.com/2013/07/01/preload-vs-eager-load-vs-joins-vs-includes.html

Upvotes: 1

Related Questions