所以我最近一直在研究单表继承,并找到了这个常见的问题/答案:
问题:如何将对象
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如何处理继承),那将是非常棒的。
答案 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)
但在大多数情况下,这同样是一件奇怪的事情。