5

I have this relationship : a user can have zero or one dog, but a dog has to belong to someone.

# dog.rb
class Dog < ActiveRecord::Base
  belongs_to :user
end

# user.rb
class User < ActiveRecord::Base
  has_one :dog
end

I want to define the following scopes :

User.with_a_dog
User.without_a_dog

I can do this for the first case, because the joins are INNER JOIN by default in rails :

scope :with_a_dog, :joins(:dog)

1/ Is this solution for the first scope good enough?

2/ What would you do for the second one?

3/ (Somewhat related) Is there a better way of doing this? :

# user.rb
def has_a_dog?
  !self.dog.nil?
end

Thanks for your help!

MrRuru
  • 1,932
  • 2
  • 20
  • 24

3 Answers3

5

Just wanted to add this in case someone finds it useful:

user.rb

class User < ActiveRecord::Base
  has_one :dog

  # To get records with a dog we just need to do a join.
  # AR will do an inner join, so only return records with dogs
  scope :with_a_dog, -> { joins(:dog) }

  # To get records without a dog, we can do a left outer join, and then only
  # select records without a dog (the dog id will be blank).
  # The merge with the Dog query ensures that the id column is correctly
  # scoped to the dogs table
  scope :without_a_dog, -> {
    includes(:dog).merge( Dog.where(:id => nil) )
  }
end

dog.rb

class Dog < ActiveRecord::Base
  belongs_to :user
end
br3nt
  • 9,017
  • 3
  • 42
  • 63
1

user.rb

scope :without_a_dog, -> {
    includes(:dog).select { |user| user.dog.nil? }
  }
justi
  • 3,887
  • 2
  • 18
  • 24
1

For question 2 I think the following should work:

scope :without_a_dog include(:dog), where('dogs.id is null')

Where include should do a left join meaning that where there's no dog relation to join to the user the dogs.id column should be null.

Chris Bailey
  • 4,126
  • 24
  • 28
  • Couldn't make it work on Rails 3.2, but this did the trick: `scope :without_profile, lambda { includes(:user_profile).where('user_profiles.id is null') }` – yorch Aug 24 '12 at 21:40