How to Determine if Rails Association is Eager Loaded?

Ruby on-RailsActiverecordEager Loading

Ruby on-Rails Problem Overview


Does anyone know a way to determine if a Rails association has been eager loaded?

My situation: I have a result set where sometimes one of the associations is eager loaded, and sometimes it isn't. If it isn't eager-loaded, then I want to look up associations using ActiveRecord's find. If it is eager loaded, I want to use detect.

For example, say that I have a "has_many" array of shipping_info objects in my item model. Then:

If item is eager loaded, most efficient load is:

item.shipping_infos.detect { |si| si.region == "United States" }

If item isn't eager loaded, most efficient load is:

item.shipping_infos.where(region: "United States").first

But unless I know whether it is eager loaded, I don't know which code to call to get the record efficiently. If I use the first method when it wasn't eager loaded, then I have to look up more DB records than necessary. And if I use the second method when it was eager loaded, then my eager loaded objects are ignored.

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

Use .association(name).loaded? on a record.


For Rails < 3.1 use loaded_foo?.

(It is deprecated since Rails 3.1. See: https://github.com/rails/rails/issues/472.)

Solution 2 - Ruby on-Rails

item.shipping_infos.loaded? will tell you.

I gotta say, though: this path leads to madness... before writing code that tests loaded? to decide between #detect and #find, make sure this instance really matters, relative to everything else that's going on.

If this isn't the slowest thing your app does, adding extra code paths adds unnecessary complexity. Just because you might waste a little database effort doesn't mean you need to fix it - it probably doesn't matter in any measurable way.

Solution 3 - Ruby on-Rails

I'd suggest using item.association_cache.keys that will provide a list of the eager loaded associations. So you item.association_cache.keys.include?(:name_of_association)

Solution 4 - Ruby on-Rails

association_cached? might be a good fit:

item.association_cached?(:shipping_infos)

Solution 5 - Ruby on-Rails

You can detect whether or not a single association has been loaded with loaded_foo?. For example, if shipping_info was a belongs_to association, then item.loaded_shipping_info? will return true when it's been eager-loaded. Oddly, it appears to return nil (rather than false) when it hasn't been loaded (in Rails 2.3.10 anyway).

Solution 6 - Ruby on-Rails

Solution to this problem should be foo.association(:bla).loaded?, BUT it works incorrectly - it checks and marks association as dirty:

class Foo; has_one :bla, :autosave => true end
foo.association(:bla).loaded? #=> false
foo.save # saves foo and fires select * from bla

So I've added following extension to ActiveRecord:

module ActiveRecord
  class Base
    def association_loaded?(name)
      association_instance_get(name).present?
    end
  end
end

and now:

class Foo; has_one :bla, :autosave => true end
foo.association_loaded?(:bla) #=> false
foo.save # saves foo

Solution 7 - Ruby on-Rails

Have a look at the Bullet gem.. This will tell you when you should and should not use eager loading.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionwbhardingView Question on Stackoverflow
Solution 1 - Ruby on-RailsgamovView Answer on Stackoverflow
Solution 2 - Ruby on-RailsBryan StearnsView Answer on Stackoverflow
Solution 3 - Ruby on-RailsdabobertView Answer on Stackoverflow
Solution 4 - Ruby on-Railsphil pirozhkovView Answer on Stackoverflow
Solution 5 - Ruby on-RailsJim StewartView Answer on Stackoverflow
Solution 6 - Ruby on-RailsLev LukomskyView Answer on Stackoverflow
Solution 7 - Ruby on-RailsBitterzoetView Answer on Stackoverflow