我多次遇到这个数据问题,并尝试了几种不同的策略。我认为我最喜欢的是 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
我喜欢这种方法的一点是,您可以将通用条目数据保留在条目模型中。将任何子条目类型数据抽象到它们自己的数据表中,并与它们有一个 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)) 将条目作为条目而不是子类处理。
希望这会有所帮助。
警告,过去我实际上使用 Media 作为模型名称。在我的情况下,它在 rails 2.3.x 中生成了一个名为 medias 的表,但是在 rails 3 中,它希望我的模型被命名为 Medium 和我的 table media。您可能需要在此命名上添加自定义变形,但我不确定。