ActiveRecord通过针对STI类的作用域构建错误类的实例

时间:2012-07-09 00:29:48

标签: ruby-on-rails scope polymorphic-associations arel 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的实例。

2 个答案:

答案 0 :(得分:4)

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

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

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

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

DiscountLineItem.build

有了ActiveRecord可以做的所有事情,这看起来似乎是一种无意义的限制,可能不会太难改变。现在你已经引起了我的兴趣:)

<强>更新

最近这是added to Rails 4.0,其中包含以下提交消息:

  

允许您执行BaseClass.new(:type =&gt;“SubClass”)以及   parent.children.build(:type =&gt;“SubClass”)或parent.build_child to   初始化STI子类。确保类名是有效的   类和它在超级类的祖先中   协会期待。

答案 1 :(得分:1)

暂时忘记build。如果您有一些LineItem l并且l.discount,那么您将获得LineItem个实例,而不是DiscountLineItem个实例。如果您想获得DiscountLineItem个实例,我建议将范围转换为方法

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

现在您将收到DiscountLineItem个实例的集合。