Reputation: 2878
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?
Upvotes: 3
Views: 3387
Reputation: 181
Rails way it to load the object and check if it's present. But still, you can access the association scope to use it without loading the associated object:
user = User.find(1)
user.association(:detail).loaded? # => false
# Triggers the SQL you need: just check if a Detail record associated with that user exists.
user.association(:detail).scope.exists?
user.association(:detail).loaded? # => false (we still didn't load the associated object)
user.detail.id # Does DB request to get Detail
user.association(:detail).loaded? # => true
Upvotes: 0
Reputation: 679
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.
Upvotes: 3
Reputation: 647
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.
user = User.find(1)
user.association(:detail).loaded? # => false
user.detail.id # Does DB request to get Detail
user.association(:detail).loaded? # => true
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
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
.first
or some other queries won't always loadKeep 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.
user = User.eager_load(:courses).find(1)
user.association(:courses).loaded? # => true
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.
Upvotes: 2