Melhor:
Person.includes(:friends).where( :friends => { :person_id => nil } )
Para o hmt, é basicamente a mesma coisa, você confia no fato de que uma pessoa sem amigos também não terá contatos:
Person.includes(:contacts).where( :contacts => { :person_id => nil } )
Atualizar
Tenho uma pergunta sobre has_one
nos comentários, então é só atualizar. O truque aqui é que includes()
espera o nome da associação, mas where
espera o nome da tabela. Para um, has_one
a associação será geralmente expressa no singular, de modo que muda, mas a where()
parte permanece como está. Portanto, se Person
apenas has_one :contact
, sua declaração seria:
Person.includes(:contact).where( :contacts => { :person_id => nil } )
Atualização 2
Alguém perguntou sobre o inverso, amigos sem pessoas. Como eu comentei abaixo, isso realmente me fez perceber que o último campo (acima: the :person_id
) não precisa estar relacionado ao modelo que você está retornando, apenas um campo na tabela de junção. Todos eles serão nil
para que possa ser qualquer um deles. Isso leva a uma solução mais simples para o acima:
Person.includes(:contacts).where( :contacts => { :id => nil } )
E, em seguida, alternar para devolver os amigos sem pessoas fica ainda mais simples, você muda apenas a classe na frente:
Friend.includes(:contacts).where( :contacts => { :id => nil } )
Atualização 3 - Rails 5
Obrigado a @Anson pela excelente solução Rails 5 (dê alguns + 1s para a resposta abaixo), você pode usar left_outer_joins
para evitar o carregamento da associação:
Person.left_outer_joins(:contacts).where( contacts: { id: nil } )
Eu o incluí aqui para que as pessoas o encontrem, mas ele merece os + 1s por isso. Ótima adição!
Atualização 4 - Rails 6.1
Agradecemos a Tim Park por apontar que, nos próximos 6.1, você poderá fazer o seguinte:
Person.where.missing(:contacts)
Graças ao post que ele vinculou também.