【问题标题】:Modeling inheritance with Ruby/Rails ORMs使用 Ruby/Rails ORM 建模继承
【发布时间】:2011-11-05 04:25:04
【问题描述】:

我正在尝试为一个简单的博客系统建模这种继承

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

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

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

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

当我将数据拆分为Entry + PostDataQuoteData 等时,我可以在这些数据中拥有belongs_to :entry 而在Entry 类中没有has_one ??? 吗?这将是在 sql 中执行此操作的标准方法,entry.post_data 可以由postdata 表中的entry_id 解决。

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

【问题讨论】:

    标签: sql ruby-on-rails activerecord join


    【解决方案1】:

    我多次遇到这个数据问题,并尝试了几种不同的策略。我认为我最喜欢的是 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。您可能需要在此命名上添加自定义变形,但我不确定。

    【讨论】:

    • 感谢您的精彩回答。但是有一个问题:我是否必须在博客中指定 has_many :articles 和 has_many :quotes ?为什么不只是 has_many :entries ?我不希望列出那么多特定类型的条目。
    • 感谢编辑标记,我的时间不多了 :)。没有 tilda,如果你想要的话,你可以简单地使用条目关联。您可以只渲染条目列表,如果您按照上述方式执行,rails 将渲染适当的部分。
    【解决方案2】:

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

    【讨论】:

    • 谢谢,但是...所有不同的条目类型将存储在哪里?不就是上面提到的“一张大表”(条目)方式吗?
    • 嗯,是的,抱歉,我没看好你,Kristian PD 方法正是你要找的。​​span>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多