'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 |