Handy scopes for associations

If your Rails project has a has-many association and you want a way of finding records that have no associated records, or, records that have at least one associated record, you might find this post useful.

Suppose you have classes Person and Friend that are associated via a has_many relationship:

class Person < ActiveRecord::Base
  has_many :friends
end

class Friend < ActiveRecord::Base
  belongs_to :person
end

How do you find persons that have no friends?

    Person.includes(:friends).where("friends.person_id IS NULL")

Or that have at least one friend

    Person.includes(:friends).where("friends.person_id IS NOT NULL")

Using Arel

To accomplish this with Arel set up the following scopes on Friend (thanks to @macfanatic for the tip),

class Friend
  belongs_to :person

  scope :to_somebody, ->{ where arel_table[:person_id].not_eq(nil) }
  scope :to_nobody,   ->{ where arel_table[:person_id].eq(nil) }
end

And then, Persons who have at least one friend:

    Person.includes(:friends).merge(Friend.to_somebody)

The friendless:

    Person.includes(:friends).merge(Friend.to_nobody)

Note: If you are on Rails 4 don't forget to append references(:friends) to these.


176 Words

2013-09-29