使用Ruby / Rails ORM建模继承

时间:2011-08-27 17:40:16

标签: sql ruby-on-rails activerecord join

我正在尝试为简单的博客系统建模此继承

博客有很多Entries,但它们的性质可能不同。我不想为Blog表建模,我关心的是条目:

  • 最简单的条目是Article titletext
  • 但是,
  • Quote没有标题,并且标题为text
  • Media有一个url和一个comment ...
  • 等...

使用Ruby on Rails对此进行建模的正确方法是什么?那是

  • 我应该使用ActiverRecord还是切换到DataMapper?
  • 我想避免使用大量空单元格的“一大表”方法

当我将数据拆分为Entry + PostData时,QuoteData等我可以在这些数据中belongs_to :entryhas_one ??? Entry类?这是在sql中执行此操作的标准方法,entry.post_data可以通过entry_id表中的postdata解析。{/ p>

编辑:我不想为Blog表建模,我可以这样做,我关心的是条目以及如何将继承映射到表格。

2 个答案:

答案 0 :(得分:2)

我多次遇到过这个数据问题,并尝试了一些不同的策略。我认为我最喜欢的那个是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 %> <!-- 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 %> 

为子表单数据键入render。子表单又可以使用accepts_nested_attributes_for + fields_for来正确传递数据。

我采用这种方法的唯一痛苦是如何处理控制器和路由助手。由于每个条目都是自己的类型,因此您必须为每种类型创建自定义控制器/路由(您可能需要这样...)或制作通用条目。如果采用通用方法,需要记住两件事。

1)您无法通过更新属性设置:type字段,您的控制器必须实例化相应的Article.new以保存它(您可以在此处使用工厂)。

2)您必须使用becomes()方法(@article.becomes(Entry))将条目用作条目而不是子类。

希望这有帮助。

警告,我实际上过去曾使用Media作为模型名称。在我的情况下,它导致了一个名为medias in rails 2.3.x的表,但是在rails 3中,它希望我的模型被命名为Medium和我的表媒体。您可能必须在此命名上添加自定义Inflection,但我不确定。

答案 1 :(得分:0)

您可以使用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