Rails - 单表继承 - 有缺陷的铸造方法?

时间:2011-07-27 18:17:55

标签: ruby ruby-on-rails-3 single-table-inheritance duck-typing

所以我最近一直在研究单表继承,并找到了这个常见的问题/答案:

  

问题:如何将对象obj的类从Alpha更改为   Beta,假设Beta<在STI的Alpha?

     

回答:ruby是一种鸭式语言,所以你不要使用cast。但您需要做的就是将“type”变量设置为“Beta”并保存对象,下次加载Alpha对象时,它将是Beta类型:

obj = Alpha.new
obj.save #now obj is of type Alpha

obj.type = "Beta"
obj.save #now obj is of type Beta

但是,这种方法对我来说似乎不起作用。虽然obj确实正确保存,但它似乎根本不起Beta对象的作用。它在不运行Beta验证的情况下保存,当我检查obj.respond_to?(:beta_method) #beta_method being a method in the beta class时,它返回false。这种方法不起作用吗?有正确的方法吗?或者我只是做错了什么?

修改

我发现当我执行Alpha.last.respond_to(:beta_method)时它返回false,而Beta.last.respond_to(:beta_method)返回true(但是Alpha.last和Beta.last都返回了相同的对象)。有趣的发展?如果有人能够详细解释这一点(关于ruby如何处理继承),那将是非常棒的。

2 个答案:

答案 0 :(得分:4)

根据您要完成的任务,您可能希望使用becomes或更改对象的类型(或直接通过update_attribute)并从数据库重新加载(或两者兼而有之。)

becomes将实例化并返回作为唯一参数传递的类的新对象,使用与前一个对象相同的属性。这将允许您调用新对象的方法。例如:

a = Alpha.last

# if you want to save the new type in the database:
a.type = 'Beta' 
a.save

b = a.becomes(Beta)
b.respond_to?(:beta_method)
=> true

我发现,调用方法的顺序很重要。如果您首先尝试使用becomes然后更改类型并保存,则不会更改数据库中的类型。

答案 1 :(得分:1)

正如你所说,Ruby不允许进行类型转换。相反,这里发生的是ActiveRecord根据type字段的值实例化不同类的实例。因此,当您将其更改为“Beta”时,从Ruby的角度来看,您并没有真正做任何事情。

但是当ActiveRecord在数据库中查找以检索记录时,它会看到新的type值并实例化Beta而不是Alpha的实例。这个过程与Ruby的关系不如特别是ActiveRecord。

根据您的情况,有时会有更优雅的方法。如果您需要将模型从一种类型“转换”为另一种类型(例如,Employee变为Manager,这有其他方法),这是一种合理的方法。在其他情况下,使用mixins或其他工具可以更好地实现目标。

如果需要,您还可以从Beta的属性创建Alpha的临时实例,如下所示:

# obj is an instance of Alpha
obj = Beta.new(obj.attributes)

但在大多数情况下,这同样是一件奇怪的事情。