【问题标题】:Rails -- Single Table Inheritance -- flawed casting approach?Rails——单表继承——有缺陷的铸造方法?
【发布时间】:2011-07-27 18:17:54
【问题描述】:

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

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

回答:ruby 是一种鸭式语言,因此您不使用强制转换。但是您需要做的就是将“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​​ 如何处理继承),那就太棒了。

【问题讨论】:

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


    【解决方案1】:

    根据您要完成的任务,您可能想要使用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,然后更改类型并保存,则不会更改数据库中的类型。

    【讨论】:

      【解决方案2】:

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

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

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

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

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

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

      【讨论】:

      • 我认为这个答案不太正确。在 OP 中,obj.type = "Beta" ; obj.save 确实确​​实更新了数据库,但内存中的版本仍将被类型转换为 Alpha。在这种情况下,obj.reload 不起作用,因为 Rails 期望它是 Alpha。但你可以证明它确实发生了变化:obj2 = Beta.find(obj.id)
      • ...但更好的是,使用obj.becomes(Beta) @Teoulas 如下所述。
      • 我从未暗示数据库未更新。我说的是通过将类型更改为“Beta”,Ruby 不在乎(它仍然有一个 Alpha 实例);正如我在下一段中所说,当 ActiveRecord 从数据库中重新检索记录时,它将构造一个 Beta 实例。 (我不记得为什么我在原始帖子中没有提到#becomes,但正如你所说,其他人提出了它。)
      猜你喜欢
      • 2011-08-01
      • 2013-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多