Mieux:
Person.includes(:friends).where( :friends => { :person_id => nil } )
Pour le hmt, c'est fondamentalement la même chose, vous comptez sur le fait qu'une personne sans amis n'aura pas non plus de contacts:
Person.includes(:contacts).where( :contacts => { :person_id => nil } )
Mettre à jour
Vous avez une question à propos has_one
des commentaires, donc juste une mise à jour. L'astuce ici est qu'il includes()
attend le nom de l'association mais where
attend le nom de la table. Pour un, has_one
l'association sera généralement exprimée au singulier, de sorte que cela change, mais la where()
partie reste telle qu'elle est. Donc, si Person
seulement has_one :contact
alors votre déclaration serait:
Person.includes(:contact).where( :contacts => { :person_id => nil } )
Mise à jour 2
Quelqu'un a demandé l'inverse, des amis sans personne. Comme je l'ai commenté ci-dessous, cela m'a fait réaliser que le dernier champ (ci-dessus: le :person_id
) n'a pas à être lié au modèle que vous renvoyez, il doit simplement s'agir d'un champ dans la table de jointure. Ils vont tous l'être, nil
donc ça peut être n'importe lequel d'entre eux. Cela conduit à une solution plus simple à ce qui précède:
Person.includes(:contacts).where( :contacts => { :id => nil } )
Et puis changer cela pour renvoyer les amis sans personne devient encore plus simple, vous ne changez que la classe à l'avant:
Friend.includes(:contacts).where( :contacts => { :id => nil } )
Mise à jour 3 - Rails 5
Merci à @Anson pour l'excellente solution Rails 5 (donnez-lui quelques + 1 pour sa réponse ci-dessous), vous pouvez utiliser left_outer_joins
pour éviter de charger l'association:
Person.left_outer_joins(:contacts).where( contacts: { id: nil } )
Je l'ai inclus ici pour que les gens le trouvent, mais il mérite les + 1 pour cela. Excellent ajout!
Mise à jour 4 - Rails 6.1
Merci à Tim Park d' avoir souligné que dans la prochaine version 6.1, vous pouvez le faire:
Person.where.missing(:contacts)
Merci aussi au post auquel il a lié.