2010-09-20 2 views
15

Grüßen,Filtering Kind-Objekte in einer has_many: durch Beziehung in Rails 3

ich eine Anwendung, wo Companies und Users müssen miteinander durch ein CompanyMembership Modell gehören, die über die Mitgliedschaft zusätzliche Informationen enthalten (genauer gesagt, ob der Benutzer ein Administrator der Firma ist, über einen booleschen Wert admin). Eine einfache Version des Codes:

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 

Natürlich, das macht es einfach, alle Mitglieder eines Unternehmens über company.users.all, et al zu erhalten. Ich versuche jedoch, eine Liste aller Benutzer in einem Unternehmen zu erhalten, die Administratoren dieses Unternehmens sind (und auch zu testen, ob ein Benutzer ein Administrator eines bestimmten Unternehmens ist). Meine erste Lösung wurde im Anschluss an die in company.rb:

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

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

Das funktioniert zwar, etwas fühlt sich ineffizient darüber (es ist über jede Mitgliedschaft laufen, SQL jedes Mal ausgeführt wird, richtig oder ist von Beziehungen mit intelligenter als das?) Und ich bin mir nicht sicher, ob es einen besseren Weg gibt, um das zu tun (vielleicht mit Scopes oder den schicken neuen Relation Objekten, die Rails 3 benutzt?).

Jeder Rat auf dem besten Weg zu verfahren (vorzugsweise mit Rails 3 Best Practices) würde sehr geschätzt werden!

Antwort

16

Ich glaube, ich war der falsche Weg, um dies geht, Bedingungen auf company_memberships statt users spezifizieren, was war, was ich eigentlich wollte (eine Liste von Users, kein Liste von CompanyMemberships). Die Lösung, die ich glaube, ich war auf der Suche nach ist:

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

, die die folgende SQL (für Unternehmen mit ID 1) erzeugt:

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') 

Ich bin nicht sicher, aber wenn ich es brauche , aber die includes() Methode wird eifrig Laden ausführen, die Anzahl der SQL-Abfragen zu halten, falls erforderlich:

Active Record können Sie in Voraus alle Verbände an, diesindwird geladen. Dies ist möglich durch Angabe der includes Methode von der Model.find Aufruf. Mit Includes, Active Record stellt sicher, dass alle angegebenen Assoziationen geladen mit der minimal möglichen Anzahl von Abfragen. Abfragen. RoR Guides: ActiveRecord Querying

(Ich bin immer noch offen für alle Vorschläge von jedem, der nicht glaubt, ist die beste/effektivste/richtige Weg, um dies zu realisieren.)

2

Wie wäre es etwa so:

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

Ein Schritt näher! Obwohl es nicht die Antwort ist, nach der ich gesucht habe, hat es mich dazu geführt :) –

7

Ein noch sauberer Weg wäre,

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

möglicherweise müssen Sie graben sich in die rails doc bekommen die genaue Syntax rechts: eine Verbindung zu Ihrem Firmen Modell, um so etwas zu addieren.

Sie sollten Folgendes nicht benötigen: include, es sei denn, Sie haben andere Klassen zugeordnet: Benutzer, auf den Sie in Ihrer Ansicht verweisen könnten.