2010-09-20 5 views
15

Salutations,enfant Filtrage des objets dans une has_many: par rapport à Rails 3

J'ai une application où Companies et Users doivent appartenir à l'autre par un modèle CompanyMembership, qui contient des informations supplémentaires sur les membres (en particulier, si l'utilisateur est ou non un administrateur de l'entreprise, via une valeur booléenne admin). Une version simple du code:

class CompanyMembership < ActiveRecord::Base 
    belongs_to :company 
    belongs_to :user 
end 

class Company < ActiveRecord::Base 
    has_many :company_memberships 
    has_many :users, :through => :company_memberships 
end 

class User < ActiveRecord::Base 
    has_many :company_memberships 
    has_many :companies, :through => :company_memberships 
end 

Bien sûr, cela rend simple d'obtenir tous les membres d'une entreprise par l'intermédiaire company.users.all, et al. Cependant, j'essaie d'obtenir une liste de tous les utilisateurs d'une société qui sont des administrateurs de cette société (et aussi de tester si un utilisateur est un administrateur d'une entreprise donnée). Ma première solution était la suivante en company.rb:

def admins 
    company_memberships.where(:admin => true).collect do |membership| 
    membership.user 
    end 
end 

def is_admin?(user) 
    admins.include? user 
end 

Bien que cela fonctionne, quelque chose se sent inefficace à ce sujet (il est itérer sur chaque membre, exécutant SQL à chaque fois, à droite ou est la relation plus intelligent que ça?), Et je Je ne suis pas sûr s'il y a une meilleure façon d'y parvenir (peut-être en utilisant des étendues ou les nouveaux objets fantaisie Relation que Rails 3 utilise?).

Tout conseil sur la meilleure façon de procéder (de préférence en utilisant les meilleures pratiques de Rails 3) serait grandement apprécié!

Répondre

16

je crois que j'allais sur ce de la mauvaise façon, spécifiant les conditions company_memberships au lieu de users, qui était ce que je voulais vraiment (une liste de Users, pas liste de CompanyMemberships). La solution que je pense que je cherchais est:

users.where(:company_memberships => {:admin => true}) 

qui génère l'instruction SQL suivante (pour la société avec ID 1):

SELECT "users".* FROM "users" 
    INNER JOIN "company_memberships" 
    ON "users".id = "company_memberships".user_id 
    WHERE (("company_memberships".company_id = 1)) 
    AND ("company_memberships"."admin" = 't') 

Je ne sais pas encore si je vais en avoir besoin , mais la méthode includes() effectuera le chargement désireux de limiter le nombre de requêtes SQL si nécessaire:

active Record vous permet de spécifier à l'avance toutes les associations qui sont va être chargé. Ceci est possible en spécifiant la méthode includes de l'appel Model.find. Avec includes, Active Record garantit que toutes les associations spécifiées sont chargées en utilisant le nombre minimum possible de requêtes.queries. RoR Guides: ActiveRecord Querying

(je suis toujours ouvert à toutes les suggestions de ceux qui pensent que ce n'est pas le meilleur/plus moyen efficace/droite pour aller à ce sujet.)

2

Que diriez-vous quelque chose comme ceci:

Company.find(:id).company_memberships.where(:admin => true).joins(:user) 
+1

Un pas de plus! Bien que ce ne soit pas la réponse que je cherchais, elle m'a conduit :) –

7

Une façon encore plus propre serait d'ajouter une association à votre modèle d'entreprise, quelque chose comme ceci:

has_many :admins, :through => :company_memberships, :class_name => :user, :conditions => {:admin => true} 

Vous pourriez avoir à creuser dans le rails doc pour obtenir le droit de syntaxe exacte.

Vous ne devriez pas avoir besoin de: include, sauf si vous avez d'autres classes associées à: user que vous pourriez référencer dans votre vue.