Mejor:
Person.includes(:friends).where( :friends => { :person_id => nil } )
Por el momento, es básicamente lo mismo, confías en el hecho de que una persona sin amigos tampoco tendrá contactos:
Person.includes(:contacts).where( :contacts => { :person_id => nil } )
Actualizar
Tengo una pregunta sobre has_one
en los comentarios, así que solo actualizando. El truco aquí es que includes()
espera el nombre de la asociación pero where
espera el nombre de la tabla. Para a, has_one
la asociación generalmente se expresará en singular, de modo que cambia, pero la where()
parte permanece como está. Entonces, si Person
solo una has_one :contact
, su declaración sería:
Person.includes(:contact).where( :contacts => { :person_id => nil } )
Actualización 2
Alguien preguntó por el inverso, amigos sin gente. Como comenté a continuación, esto realmente me hizo darme cuenta de que el último campo (arriba: el :person_id
) en realidad no tiene que estar relacionado con el modelo que está devolviendo, solo tiene que ser un campo en la tabla de unión. Todos van a ser nil
así que puede ser cualquiera de ellos. Esto lleva a una solución más simple a lo anterior:
Person.includes(:contacts).where( :contacts => { :id => nil } )
Y luego cambiar esto para devolver a los amigos sin gente se vuelve aún más simple, solo cambias la clase en el frente:
Friend.includes(:contacts).where( :contacts => { :id => nil } )
Actualización 3 - Rails 5
Gracias a @Anson por la excelente solución Rails 5 (darle algunos +1 por su respuesta a continuación), puede usar left_outer_joins
para evitar cargar la asociación:
Person.left_outer_joins(:contacts).where( contacts: { id: nil } )
Lo he incluido aquí para que la gente lo encuentre, pero se merece los +1 por esto. Gran adición!
Actualización 4 - Rails 6.1
Gracias a Tim Park por señalar que en el próximo 6.1 puedes hacer esto:
Person.where.missing(:contacts)
Gracias a la publicación a la que también se vinculó.