使用Ruby / Rails ORM建模inheritance
我正在尝试为一个简单的博客系统建模这个inheritance
博客有许多Entries
,但它们的性质可能不同。 我不想为Blog表建模,我关心的是条目:
- 最简单的条目是具有
title
和text
- 但是,
Quote
没有标题并且有短文 -
Media
有url
和comment
…… - 等等…
使用Ruby on Rails对此进行建模的正确方法是什么? 那是
- 我应该使用ActiverRecord还是切换到DataMapper?
- 我想避免使用大量空单元格的“一大表”方法
当我将数据拆分为Entry
+ PostData
, QuoteData
等我可以在这些数据中拥有belongs_to :entry
而没有has_one ???
在Entry
课程? 这将是在sql和entry.post_data
执行此操作的标准方法。可以通过postdata
表中的postdata
解析postdata
。
编辑:我不想模拟Blog表,我可以这样做,我关心的是条目以及如何将inheritance映射到表。
我已经多次遇到过这个数据问题,并尝试了一些不同的策略。 我认为我最喜欢的那个是cicloon提到的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
我喜欢这种方法,你可以在入门模型中保存通用的Entry数据。 将任何子条目类型数据抽象到它们自己的数据表中,并且与它们具有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 %>
或者有更多的控制权:
<%= render @blog.quotes %> <%= render @blog.articles %>
您也可以找到生成表单的非常通用的方法,我通常在entries/_form.html.erb
partial中呈现通用条目字段。 在那部分里面,我也有一个
<%= form_for @entry do |f| %> <%= render :partial => "#{f.object.class.name.tableize}/#{f.object.class.name.underscore}_form", :object => f %> <% end %>
为子表单数据键入render。 子表单又可以使用accepts_nested_attributes_for
+ fields_for
来正确传递数据。
我采用这种方法的唯一痛苦是如何处理控制器和路由助手。 由于每个条目都是自己的类型,因此您必须为每种类型创建自定义控制器/路由(您可能需要这样...)或制作通用条目。 如果采用通用方法,需要记住两件事。
1)你不能通过更新属性设置:type
字段,你的控制器必须实例化相应的Article.new
来保存它(你可以在这里使用工厂)。
2)您必须使用@article.becomes(Entry)
becomes()
方法( @article.becomes(Entry)
)将条目作为Entry而不是子类。
希望这可以帮助。
警告,我实际上过去曾使用Media作为模型名称。 在我的情况下,它导致了一个名为medias in rails 2.3.x的表,但是在rails 3中,它希望我的模型被命名为Medium和我的表媒体。 您可能必须在此命名上添加自定义Inflection,但我不确定。
您可以使用ActiveRecord STI轻松处理此问题。 它要求您在条目表中有一个类型字段。 这样您可以像这样定义模型:
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