【问题标题】:Prevent STI when inheriting from an ActiveRecord model从 ActiveRecord 模型继承时防止 STI
【发布时间】:2012-07-26 19:02:54
【问题描述】:

在 Rails 3.2.6 上,我有一个类继承自 ActiveRecord::Base:

class Section < ActiveRecord::Base
  ...
end

当我从这个类继承时,Rails 会假设我想要 STI:

class AnotherSection < Section
   ..Rails assumes I have a type field, etc...
end

我希望能够继承 Section 类并将子类用作普通的 Ruby 子类,而无需使用 Rails STI 魔法。

ActiveRecord::Base 模型子类化时,有没有办法防止 STI?

【问题讨论】:

  • 如果您没有不应该打扰您的type 列...如果您确实有type,那么您可以按照@Veraticus 所说的方式禁用它。
  • 其实你还是有STI的:两个类的实例会存储在同一张表中,STI(单表继承)的定义是什么。您只是不想有一个鉴别器列(“类型”)。但是,您如何知道section 中的每条记录是普通section 还是AnotherSection?

标签: ruby-on-rails ruby sti


【解决方案1】:

您可以通过禁用模型的inheritance_column 来实现此目的,如下所示:

class AnotherSection < Section
  # disable STI
  self.inheritance_column = :_type_disabled

end

【讨论】:

  • 那个,或者任何不存在的列就足够了。
  • self.inheritance_column = nil 为我工作(但我很久以前尝试过,它是 rails 3.2)
  • 这将禁用鉴别器列。但是两个类都存储在同一张表中,STI(单表继承)的定义是什么。您刚刚删除了鉴别器列,ruby 将无法确定每个存储记录的类型(您需要在加载时决定)
【解决方案2】:

接受的答案肯定会起作用,但推荐的(我敢说“正确”:) 方法是设置abstract_class:

class Section < ActiveRecord::Base
  self.abstract_class = true
end

【讨论】:

  • 这是正确的方法,它从 Rails 1.1 开始就存在了。
  • 这对我不起作用。我无法实例化这个模型类的任何对象......(NotImplementedError: MyModel is an abstract class and cannot be instantiated.)。所以请修改你的答案@smathy
  • @deepflame 你不能实例化一个抽象类,它是抽象的。
  • 感谢您的回复。是的,我只是想从具有“类型”列并且没有正确阅读上述问题的模型中禁用 STI。
【解决方案3】:

在 ActiveRecord 上存储继承的唯一完全受支持的策略是 STI。但是,您可以自担风险模拟具体的类表继承。正如 smathy 所指出的,具有抽象超类的具体类表继承工作正常。

但是...如果您想要使 AnotherSection 只是一个普通的类(不会在数据库中持久化),您可以禁用鉴别器列(如 Veraticus 所建议的那样)。但是,如果您保存 AnotherSection,它将与 Section 保存在同一个表中,您将无法区分它们。另外,如果你使用 AnotherSection 来寻找一个 Section,它会返回一个 AnotherSection,打破原来的实例化:

    #create a Section and saves it
    sect = Section.create()
    sect.save() 
    #retrieve the Section as a AnotherSection, breaking polymorphism... 
    sect = AnotherSection.find(sect.id)
    # another section is more than a section, it is inconsistent.

如果AnotherSection不打算被持久化,它覆盖持久化操作的最安全路径,例如save()和find():

    class AnotherSection < Section
       # disable STI, as pointed by Veraticus
       self.inheritance_column = :_type_disabled
       # disable save and finding
       def save(*args)
         #exception? do nothing?
       end
       def find(*args)
         #exception? do nothing?
       end
       def find_by(*args)
         #exception? do nothing?
       end
       # this does not stops here! there is first, last, and even a forty_two finder method! not to mention associations...
    end

简而言之,您可以这样做,但您不应该这样做。风险很高。 您应该考虑另一种选择,例如使用 MIXIN 而不是继承。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多