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.
请注意,目前这个宝石测试得不是很好,尽管它可以在我的用例中使用.