ActiveRecord通过一个以STI类为目标的作用域来构建错误类的实例



我希望能够在通过STI类型针对某个模型类的作用域上调用build方法,并让ActiveRecord构建正确类的实例。

class LineItem < ActiveRecord::Base
  scope :discount, where(type: 'DiscountLineItem')
end
class DiscountLineItem < LineItem; end
> LineItem.discount.build # Expect an instance of DiscountLineItem here
=> #<LineItem ...>

在这里,我期望的是DiscountLineItem的实例,而不是LineItem的实例。

即使ActiveRecord没有将对象实例化为正确的类,但它确实正确地设置了类型。你基本上有两种方法:

1) 创建对象,然后从数据库中重新加载:

item = LineItem.discount.create(attrs...)
item = LineItem.find(item.id)

2) 使用STI类并直接从中构建对象:

DiscountLineItem.build

有了ActiveRecord所能做的一切,这似乎是一种毫无意义的限制,可能不太难改变。现在你引起了我的兴趣:)

更新:

这是最近添加到Rails 4.0中的,并带有以下提交消息:

允许您执行BaseClass.new(:type=>"SubClass")以及parent.children.build(:type=>"SubClass")或parent.build_child to初始化STI子类。确保类名是有效的阶级,而正是在超级阶级的祖先中正在期待关联。

暂时忘记build。如果你有一些LineItem l,而你有l.discount,你会得到LineItem实例,而不是DiscountLineItem实例。如果你想获得DiscountLineItem实例,我建议将作用域转换为方法

def self.discount
  where(type: 'DiscountLineItem').map { |l| l.becomes(l.type.constantize) }
end

现在,您将返回一个DiscountLineItem实例的集合。

最新更新