'how to check if has_one exists without loading associated model

I have a simple has_one relationship

class User < ApplicationRecord
  has_one :detail
  has_many :courses
end

class Detail < ApplicationRecord
  belongs_to :user
end

I need to check to see if a user has a detail. Since I can do User.first.courses.exists? it was surprising that User.first.detail.exists? doesn't work. I could do !User.first.detail.nil? but I only need to check if a user detail exists and not load it's very hefty detail model.

What's a good way to check if a has_one association exists without retrieving the entire associated model?



Solution 1:[1]

You can use: User.first.detail.present?

Solution 2:[2]

Detail.where(user_id: User.first.id).exists?

This doesn't allocate a Detail object if all you want to know is if a user has a detail or not.

Solution 3:[3]

To check if an association is loaded without hitting the DB you can use:

model.association(:association_name).loaded? for has_one/belongs_to associations and model.many_things.loaded? for has_many associations.

Checking if has_one/belongs_to association has been loaded:

user = User.find(1)

user.association(:detail).loaded? # => false

user.detail.id # Does DB request to get Detail

user.association(:detail).loaded? # => true
With Eager loading

Eager loading lets you load the data in advance, so as expected the association is marked as loaded:

user = User.eager_load(:detail).find(1)
user.detail.loaded? # => true

Checking if has_many association has been loaded

With has_many associations,

user = User.find(1)
user.association(:courses).loaded? # => false

user.courses.load # Does DB query to get all users' courses
user.association(:courses).loaded? # => true
Doing .first or some other queries won't always load

Keep in mind that it does not mark as loaded in all cases:

user.courses.first.id # Does DB query to get first course ID
user.association(:courses).loaded? # => false (still!)

This is because it is actually doing a SELECT * LIMIT 1 query when you do the .first for efficiency-sake.

With Eager loading
user = User.eager_load(:courses).find(1)
user.association(:courses).loaded? # => true

When to use the loaded? method?

The only use case I can think of is when you don't want to trigger a DB call but you want to check to see if the data has been loaded already and do something with it only if it has.

In my case I wanted to output some data for logging purposes, but only if it was not adding an extra DB query by calling the association.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Huynh
Solution 2
Solution 3 Jay El-Kaake