Лучше:
Person.includes(:friends).where( :friends => { :person_id => nil } )
Для hmt это в основном то же самое, вы полагаетесь на то, что у человека без друзей также не будет контактов:
Person.includes(:contacts).where( :contacts => { :person_id => nil } )
Обновить
Есть вопрос о has_one
в комментариях, так что просто обновление. Хитрость в том, чтоincludes()
ожидает имя ассоциации, но where
ожидает имя таблицы. Для has_one
ассоциации обычно будет выражаться в единственном числе, так что меняется, но where()
часть остается такой, как есть. Так что, если Person
только has_one :contact
тогда ваше заявление будет:
Person.includes(:contact).where( :contacts => { :person_id => nil } )
Обновление 2
Кто-то спрашивал об обратном, друзья без людей. Как я прокомментировал ниже, это фактически заставило меня осознать, что последнее поле (выше :person_id
:) не обязательно должно быть связано с возвращаемой моделью, оно просто должно быть полем в таблице соединений. Они все будут, nil
так что это может быть любой из них. Это приводит к более простому решению вышеперечисленного:
Person.includes(:contacts).where( :contacts => { :id => nil } )
И затем переключение на возвращение друзей без людей становится еще проще, вы меняете только класс впереди:
Friend.includes(:contacts).where( :contacts => { :id => nil } )
Обновление 3 - Rails 5
Спасибо @Anson за отличное решение для Rails 5 (дайте ему +1 к ответу ниже), вы можете использовать left_outer_joins
чтобы избежать загрузки ассоциации:
Person.left_outer_joins(:contacts).where( contacts: { id: nil } )
Я включил это здесь, чтобы люди нашли это, но он заслуживает +1 для этого. Отличное дополнение!
Обновление 4 - Rails 6.1
Спасибо Tim Park за указание, что в следующей версии 6.1 вы можете сделать это:
Person.where.missing(:contacts)
Благодаря посту, на который он тоже ссылался.