Краще:
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
Хтось запитав про зворотне, друзів, у яких немає людей. Як я коментував нижче, це насправді дало мені зрозуміти, що останнє поле (вище: the :person_id) насправді не повинно бути пов’язане з моделлю, яку ви повертаєтесь, воно просто повинно бути полем в таблиці приєднання. Вони все будуть, nilщоб це було будь-яким із них. Це призводить до більш простого рішення вищезазначеного:
Person.includes(:contacts).where( :contacts => { :id => nil } )
А потім переключити це на повернення друзів без людей стає ще простіше, ви змінюєте лише клас спереду:
Friend.includes(:contacts).where( :contacts => { :id => nil } )
Оновлення 3 - рейки 5
Завдяки @Anson за відмінне рішення Rails 5 (дайте йому декілька +1 для його відповіді нижче), ви можете використовувати, left_outer_joinsщоб не завантажувати асоціацію:
Person.left_outer_joins(:contacts).where( contacts: { id: nil } )
Я включив його сюди, щоб люди знайшли його, але він заслуговує +1 для цього. Чудове доповнення!
Оновлення 4 - Рейки 6.1
Дякуємо Тім Парку , що вказали, що в майбутньому 6.1 ви можете це зробити:
Person.where.missing(:contacts)
Завдяки публікації, з якою він також посилався.