2011-08-27 55 views
0

我想這種繼承用的Ruby/Rails的ORM建模繼承

博客模型一個簡單的博客系統有許多Entries,但他們可能會在他們的性質不同。我不想博客表的模型,我關心的是有關的條目:

  • 簡單的條目是Articletitletext
  • Quote,但是,沒有一個標題和短text
  • Mediaurlcomment ...
  • 等...

什麼是用Ruby on Rails建模的正確方法?那是

  • 我應該使用ActiverRecord進行此操作還是切換到DataMapper?
  • 我想避免與大量的空單元格

的「一臺大桌」的做法。當我將數據分割成Entry + PostDataQuoteData等我可以在這些數據單項belongs_to :entry不具有has_one ???Entry類?這將是在SQL中執行此操作的標準方法,並且entry.post_data可能由表中的entry_id解決。

編輯:我不想模型的博客表,我可以做到這一點,我的關注是關於條目以及如何將繼承映射到表(s)。

回答

2

我遇到過這個數據問題幾次,並嘗試了幾種不同的策略。我認爲我最大的粉絲之一就是CCI提到的STI方法。確保您的入口表上有一個type列。

class Blog < ActiveRecord::Base 
    # this is your generic association that would return all types of entries 
    has_many :entries 

    # you can also add other associations specific to each type. 
    # through STI, rails is aware that a media_entry is in fact an Entry 
    # and will do most of the work for you. These will automatically do what cicloon. 
    # did manually via his methods. 
    has_many :articles 
    has_many :quotes 
    has_many :media 
end 

class Entry < ActiveRecord::Base 
end 

class Article < Entry 
    has_one :article_data 
end 

class Quote < Entry 
    has_one :quote_data 
end 

class Media < Entry 
    has_one :media_data 
end 

class ArticleData < ActiveRecord::Base 
    belongs_to :article # smart enough to know this is actually an entry 
end 

class QuoteData < ActiveRecord::Base 
    belongs_to :quote 
end 

class MediaData < ActiveRecord::Base 
    belongs_to :media 
end 

我喜歡這種方式的事情,是你可以保持通用輸入數據的入門車型。將任何子條目類型數據抽象到它們自己的數據表中,並將它們與has_one關聯起來,從而不會在條目表上產生額外的列。它也工作得很好,當你做你的意見爲:

app/views/articles/_article.html.erb 
app/views/quotes/_quote.html.erb 
app/views/media/_media.html.erb # may be medium here.... 

,並從你的觀點,你可以做兩種:

<%= render @blog.entries %> <!-- this will automatically render the appropriate view partial --> 

或有更多的控制:

<%= render @blog.quotes %> 
<%= render @blog.articles %> 

你可以找到一種非常通用的生成表單的方式,我通常會在entries/_form.html.erb部分中顯示通用輸入字段。在該部分中,我也有一個

<%= form_for @entry do |f| %> 
    <%= render :partial => "#{f.object.class.name.tableize}/#{f.object.class.name.underscore}_form", :object => f %> 
<% end %> 

爲子表單數據類型呈現。子表單反過來可以使用accepts_nested_attributes_for + fields_for來正確傳遞數據。

我對這種方法唯一的痛苦是如何處理控制器和路由幫助器。由於每個條目都是自己的類型,因此您必須爲每種類型創建自定義控制器/路由(您可能需要這樣...)或創建一個通用控制器。如果你採用通用方法,需要記住兩件事。

1)你不能通過更新屬性來設置:type字段,你的控制器將不得不實例化合適的Article.new來保存它(你可以在這裏使用一個工廠)。

2)您將不得不使用becomes()方法(@article.becomes(Entry))將條目作爲條目而不是子類使用。

希望這會有所幫助。

警告,我實際上在過去使用過媒體作爲模型名稱。在我的情況下,它導致了一個名爲medias的表格,但是在rails 3中,它希望我的模型被命名爲Medium和我的表格媒體。儘管我不確定,但您可能必須在此命名中添加一個自定義的拐點。

+0

感謝您的回答。但有一個問題:我必須在Blog中指定has_many:articles和has_many:引號嗎?爲什麼不只是has_many:條目?我不希望列舉某種特定類型的條目。 – tillda

+0

感謝您的編輯標記,我沒有時間:)。 沒有蒂爾達,你可以簡單地使用條目關聯,如果這只是你想要的。如果以上述方式進行操作,則可以渲染條目列表,並且軌道將呈現適當的部分。 –

0

您可以使用ActiveRecord STI輕鬆處理。它要求你在你的Entries表中有一個類型字段。這樣你可以這樣定義你的模型:

def Blog > ActiveRecord::Base 
    has_many :entries 

    def articles 
    entries.where('Type =', 'Article') 
    end 

    def quotes 
    entries.where('Type =', 'Quote') 
    end 

    def medias 
    entries.where('Type =', 'Media') 
    end 

end 

def Entry > ActiveRecord::Base 
    belongs_to :blog 
end 

def Article > Entry 
end 

def Quote > Entry 
end 

def Media > Entry 
end 
+0

謝謝,但......所有**不同**入口類型的存儲位置在哪裏?是不是所提到的「一張大桌子」(條目)的方法? – tillda

+0

嗯對不起,我沒讀過你很好,克里斯蒂安PD方法就是你要找的。 – cicloon