2009-04-22 1 views
1

Ich versuche zu verstehen, wie ActiveRecord Verbindungen behandelt, die komplexer sind als einfache has_many, belongs_to, und so weiter.Komplexe Assoziationen in ActiveRecord-Modellen

Betrachten Sie als Beispiel eine Anwendung zum Aufzeichnen von Musikauftritten. Jeder Gig hat eine Band mit einem Genre. Jeder Gig hat auch einen Veranstaltungsort, der eine Region hat.

In der rauen Notation von MS Access (die ich plötzlich das Gefühl ganz nostalgisch fange an) diese Beziehungen würde wie folgt dargestellt werden

 1 ∞  1 ∞  ∞ 1  ∞ 1 
Genre ---- Band ---- Gig ---- Venue ---- Region 

Ich mag wäre in der Lage sein, zu erfahren, zum Beispiel , alle Bands, die in einer Region gespielt haben, oder alle Orte, die ein bestimmtes Genre beherbergen.

Idealerweise meine Modelle enthalten würde diesen Code

class Genre 
    has_many :bands 
    has_many :gigs, :through => bands 
    has_many :venues, :through => :gigs, :uniq => true 
    has_many :regions, :through => :venues, :uniq => true 
end 

class Band 
    belongs_to :genre 
    has_many :gigs 
    has_many :venues, :through => :gigs, :uniq => true 
    has_many :regions, :through => :venues, :uniq => true 
end 

class Gig 
    belongs_to :genre, :through => :band 
    belongs_to :band 
    belongs_to :venue 
    belongs_to :region, :through => :venue 
end 

und so weiter für Venue und Region.

aber es scheint, ich habe etwas zu produzieren, wie dies stattdessen

class Genre 
    has_many :bands 
    has_many :gigs, :through => bands 
    has_many :venues, :finder_sql => "SELECT DISTINCT venues.* FROM venues " + 
    "INNER JOIN gigs ON venue.id = gig.venue_id " + 
    "INNER JOIN bands ON band.id = gig.band_id " + 
    "WHERE band.genre_id = #{id}" 
    # something even yuckier for regions 
end 

class Band 
    belongs_to :genre 
    has_many :gigs 
    has_many :venues, :through => :gigs, :uniq => true 
    # some more sql for regions 
end 

class Gig 
    delegate :genre, :to => :band 
    belongs_to :band 
    belongs_to :venue 
    delegate :region, :to => :venue 
end 

Ich habe zwei Fragen - eine allgemeine und eine besondere.

Die allgemeinen:

Ich hätte gedacht, dass das, was ich versuche, ziemlich oft zu tun würde kommen. Ist das wirklich der beste Weg, oder ist da etwas viel Einfacheres, das ich übersehe?

Das besondere:

Was ich oben haben eigentlich nicht ganz funktionieren! Die #{id} im zweiten Genre-Modell tatsächlich die ID der Klasse zurückgeben. (Ich denke). Allerdings scheint dies zu funktionieren here und here

Ich realisiere, das ist eine ziemlich epische Frage, also danke, wenn Sie so weit gekommen sind. Jede Hilfe würde sehr geschätzt werden!

Antwort

0

Für solche Assoziationen werden Sie am Ende benutzerdefiniertes SQL schreiben - es gibt keine echte Möglichkeit, mit einer Kette von Assoziationen umzugehen, ohne einige ziemlich massive Joins durchführen zu müssen, und das ist es wirklich nicht ein effizienter Weg für die integrierten Abfragegeneratoren, um es mit einem Einzeiler zu handhaben.

Sie können auch in den: Joins-Parameter von ActiveRecord schauen - dies kann tun, was Sie wollen.

3

Assoziationen sind so konzipiert, dass sie lesbar sind und beschreibbar. Ein großer Teil ihres Wertes ist, dass man so etwas tun kann:

@band.gigs << Gig.new(:venue => @venue) 

Es klingt, obwohl, wie Sie wollen etwas, das nur gelesen wird. Mit anderen Worten, Sie möchten Veranstaltungsorte und Genres zuordnen, aber Sie würden es nie tun:

, weil es keinen Sinn ergeben würde. Ein Venue hat nur ein Genre, wenn eine Band mit diesem bestimmten Genre dort einen Gig hat.

Assoziationen funktionieren nicht, weil sie beschreibbar sein müssen. Hier ist, wie ich nur lesbar Verbände tun würde:

class Genre 
    has_many :bands 

    def gigs 
    Gig.find(:all, :include => 'bands', 
      :conditions => ["band.genre_id = ?", self.id]) 
    end 

    def venues 
    Venue.find(:all, :include => {:gigs => :band}, 
     :conditions => ["band.genre_id = ?", self.id]) 
    end 
end 
0

Klingt wie ein Job für nested_has_many_through! Tolles Plugin, mit dem Sie ineinander verschachtelt werden können has_many :throughs

1

Sie können Ihren Assoziationen Bedingungen und Parameter hinzufügen. Aktuelle Versionen von ActiveRecord geben die Leistung von named_scopes, die auch auf verknüpften Datensätzen funktioniert.

Von einem aktuellen Projekt

Folder has_many Pages 
Page has_many Comments 

# In Page 
named_scope :commented, 
    :include => "comments", 
    :conditions => ["comments.id IS NULL OR comments.id IS NOT NULL"], 
    :order => "comments.created_at DESC, pages.created_at DESC" 

diese verwenden wir sagen können:

folder.pages.commented 

denen Umfang auf den dazugehörigen Aufzeichnungen, eine bedingte mit den angegebenen Parametern zu tun.

Plus! named_scopes sind zusammensetzbar.

Und mehr Bereiche:

named_scope :published, :conditions => ["forum_topics.status = ?", "published"] 

und Kette sie zusammen: folder.pages.published.commented