2010-01-25 6 views
17

Ich habe ein Modell, das ein Content Artikel darstellt, der einige Bilder enthält. Die Anzahl der Bilder ist festgelegt, da diese Bildreferenzen sehr spezifisch für den Inhalt sind. Zum Beispiel bezieht sich das Modell Content zweimal auf das Modell Image (Profilbild und Hintergrundbild). Ich versuche, ein generisches has_many zu vermeiden, und bleib bei mehreren has_one. Die aktuelle Datenbankstruktur sieht so aus:Rails has_one vs gehört zu Semantik

contents 
    - id:integer 
    - integer:profile_image_id 
    - integer:background_image_id 

images 
    - integer:id 
    - string:filename 
    - integer:content_id 

Ich kann einfach nicht herausfinden, wie man die Assoziationen hier richtig einrichtet. Das Content Modell könnte zwei belongs_to Verweise auf eine Image enthalten, aber das scheint nicht semantisch richtig zu sein, weil idealerweise ein Bild zum Inhalt gehört, oder mit anderen Worten, der Inhalt hat zwei Bilder.

Dies ist das Beste, was ich von (durch die Semantik zu brechen) einfiel:

class Content 
    belongs_to :profile_image, :class_name => 'Image', :foreign_key => 'profile_image_id' 
    belongs_to :background_image, :class_name => 'Image', :foreign_key => 'background_image_id' 
end 

Bin ich weg, und es ein besserer Weg, diese Vereinigung zu erreichen?

Antwort

22

Die einfache Antwort ist Ihre Assoziationen zu Setup in umgekehrter Richtung von dem, was Sie haben, wie so:

# app/models/content.rb 
class Content < ActiveRecord::Base 
    has_one :profile_image, :class_name => 'Image' 
    has_one :background_image, :class_name => 'Image' 
end 

# app/models/image.rb 
class Image < ActiveRecord::Base 
    belongs_to :content 
end 

Sie nicht über den Fremdschlüssel ‚background_image_id‘ müssen und ‚profile_image_id‘ in der Inhaltstabelle überhaupt .

Allerdings gibt es eine elegantere Lösung: Single Table Vererbung. Richten Sie es jetzt ein, wenn Sie möchten, dass Hintergrund- und Profilbilder sich in Zukunft sogar noch etwas anders verhalten, und das wird Ihren Code heute verdeutlichen.

zunächst eine Spalte, um Ihre Bilder Tabelle namens Typ hinzufügen:

# command line 
script/generate migration AddTypeToImages type:string 
rake db:migrate 

Jetzt Setup Ihre Modelle wie folgt aus:

# app/models/content.rb 
class Content < ActiveRecord::Base 
    has_one :profile_image 
    has_one :background_image 
end 

# app/models/image.rb 
class Image < ActiveRecord::Base 
    belongs_to :content 
end 

# app/models/background_image.rb 
class BackgroundImage < Image 
    # background image specific code here 
end 

# app/models/profile_image.rb 
class ProfileImage < Image 
    # profile image specific code here 
end 

Jetzt können Sie alle möglichen Dinge tun wie immer eine Liste von Alle Hintergrundbilder:

# script/console 
BackgroundImage.all 

Dies entspricht eher dem Datenmodell, das Sie erstellen möchten, ermöglicht die einfachste Erweiterbarkeit in der Zukunft und gibt Ihnen heute einige coole neue Methoden.

UPDATE:

Ich habe da einen Blog-Artikel erstellt Single-Table Inheritance with Tests genannt, die mehr ins Detail geht und deckt Tests.

+0

Single Table Vererbung funktioniert perfekt für mein Problem. Weißt du, wie ich STI mit polymorphen Assoziationen integrieren würde? Wenn beispielsweise zusammen mit dem Inhalt einige Bilder Teil der Dokumentmetadaten waren, wurden beide - Inhalts- und Metadatenbilder in der Bildtabelle gespeichert. – Anurag

+0

vielen Dank für den Artikel. das muss der beste Artikel zur Single Table Inheritance für Rails im Netz sein! – Anurag

+0

Wie würde Content für die "einfache Antwort" wissen, auf welche Bilder verwiesen werden soll, wenn in seiner db-Tabelle keine Fremdschlüssel vorhanden sind? – greg7gkb

1

Basiert auf the AR associations guide, ich denke, Sie sollten has_one verwenden. Es macht keinen Sinn, dass ein Bild einen Inhalt besitzt ... der Inhalt besitzt das Bild. Von der Führung:

Die Unterscheidung ist in dem Sie den Fremdschlüssel platzieren (es auf den Tisch für die Klasse geht die belongs_to Vereinigung erklärt), aber Sie sollten einige Gedanken über die tatsächliche Bedeutung geben von die Daten auch. Die has_one Beziehung sagt, dass etwas von dir gehört - das ist, dass etwas auf Sie zurückweist.

Schließlich bin ich nicht sicher, dass Sie beide Inhalte und Bilder benötigen, um fremde Schlüssel zu haben. Solange die Bilder auf content_id verweisen, denke ich, dass es dir gut geht.