Ruby on Rails中的多表继承与单表继承

Ton*_*ony 11 ruby-on-rails single-table-inheritance

在过去的几个小时里,我一直在努力思考我应该走哪条路线.我有一个通知模型.到目前为止,我已经使用了notification_type列来管理类型,但我认为最好为通知类型创建单独的类,因为它们的行为方式不同.

现在,有3种方式可以发送通知:短信,推特,电子邮件

每个通知都有:

id
subject
message
valediction
sent_people_count
deliver_by
geotarget
event_id
list_id
processed_at
deleted_at
created_at
updated_at
Run Code Online (Sandbox Code Playgroud)

似乎STI是一个很好的候选人吧?当然Twitter/SMS不会有主题,Twitter也不会有sent_people_count,告别.在这种情况下,我会说他们分享他们的大部分领域.但是,如果我为twitter添加一个"reply_to"字段,为DM添加一个布尔值呢?

我的观点是,现在STI是有道理的,但这是一个案例,我可能会在未来踢我自己,而不仅仅是从MTI开始?

为了使事情更复杂,我想要一个通讯模型,这是一种通知,但区别在于它不会使用event_id或deliver_by.

我可以使用大约2/3的通知基类字段来查看通知的所有子类.STI是不是很明智,或者我应该使用MTI?

EmF*_*mFi 20

您是否考虑过混合模型方法?

您在核心通知字段中使用单表继承的位置.然后将所有唯一项卸载到与您的通知子类属于/有一个关系的特定表/模型中.

这是一个设置的开销,但是一旦定义了所有的类和表,它就会变得相当干净.似乎是一种非常有效的存储方式.通过急切加载,您不应该对数据库造成太多额外压力.

出于此示例的目的,我们假设电子邮件没有唯一的详细信息.这是它如何映射出来的.

class Notification < ActiveRecord::Base
  # common methods/validations/associations
  ...

  def self.relate_to_details
    class_eval <<-EOF
      has_one :details, :class_name => "#{self.name}Detail"
      accepts_nested_attributes_for :details
      default_scope -> { includes(:details) }
    EOF
  end
end

class SMS < Notification
  relate_to_details

  # sms specific methods
  ...
end

class Twitter < Notification
  relate_to_details

  # twitter specific methods
  ...
end

class Email < Notification

  # email specific methods
  ...
end

class SMSDetail < ActiveRecord::Base
  belongs_to :SMS, :class_name => "SMS"           

  # sms specific validations
  ...
end

class TwiterDetail < ActiveRecord::Base
  belongs_to :twitter

  # twitter specific validations
  ...
end
Run Code Online (Sandbox Code Playgroud)

每个详细信息表都将包含通知ID,并且只包含通信表中未包含的通信需求列.虽然这意味着要获取媒体特定信息的额外方法调用.

这很好知道,但你认为这是必要的吗?

在设计方面很少需要.随着CPU和存储空间的成本下降,那些必要的设计概念也是如此.我提出这个方案是因为它提供了STI和MTI的最佳性能,并消除了它们的一些弱点.

至于优势:

该方案提供了STI的一致性.使用不需要重新创建的表.链接表大约有几十列,在75%的行中都是空的.您还可以轻松创建子类.如果您的新类型未完全覆盖基本通知字段,则只需创建匹配的详细信息表.它还可以简单地迭代所有通知.

通过MTI,您可以节省存储空间并轻松定制以满足类的需求,而无需为每种新通知类型重新定义相同的列.只有独特的.

然而,该计划也带来了STI的主要缺陷.该表将取代4.一旦它变大,它就会开始导致减速.

简短的回答是,没有必要采用这种方法.我认为这是有效处理问题最干的方式.在很短的时间内,STI就是这样做的.从长远来看,MTI是要走的路,但我们谈论的是你达到数百万次通知的地步.这种方法是一些很容易扩展的好中间地带.

详细的宝石

我为你的解决方案构建了一个宝石:https://github.com/czaks/detailed.使用它,您可以将Notification类简化为:

class Notification < ActiveRecord::Base
  include Detailed
end
Run Code Online (Sandbox Code Playgroud)

其余的都是以前的方式.

作为额外的好处,您现在可以直接访问(读取,写入,关联)特定于子类的属性:notification.phone_number,而无需诉诸:notification.details.phone_number.您还可以在主类和子类中编写所有代码,将Details模型留空.您还可以使用Notification.all_with_details而不是常规对大型数据集执行更少的查询(在上面的示例4而不是N + 1中)Notification.all.

请注意,目前这个宝石测试得不是很好,尽管它可以在我的用例中使用.


小智 3

鉴于信息有限,我建议坚持 STI。

关键问题是:您的应用程序中是否有需要同时考虑所有类型通知的地方?如果是这样,那么这是一个强烈的信号,表明您想坚持感染性传播感染。