2012-07-09 55 views
2

我希望能夠通過其STI類型針對特定類型的模型調用build方法,並讓ActiveRecord構建一個實例正確的課程。ActiveRecord通過瞄準STI類的作用域構建錯誤類的實例

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一個實例。

回答

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(:類型=>「子類「)以及 parent.children.build(:type =>」SubClass「)或parent.build_child到 初始化STI子類。確保類名稱是有效的 類,並且它在 關聯期望的超級類別的祖先中。

+0

也許現在是時候把這個討論放到Rails跟蹤器中了。我會寫一個失敗的測試用例,並在今天發佈。 – steveluscher 2012-07-09 17:26:52

+0

我昨晚編了一個修復程序,並將它發佈到rails核心谷歌小組以獲得反饋。我將在今晚晚些時候發佈這裏作爲更新,當我有機會的時候。 – 2012-07-09 17:39:44

+0

@steveluscher以下是您可能想要嘗試的ActiveRecord補丁:https://gist.github.com/5cad22a11f011052d8f6 – 2012-07-10 02:25:40

1

暫時忘掉build片刻。如果你有一些LineIteml,你做l.discount你會得到LineItem實例,而不是DiscountLineItem實例。如果你想獲得DiscountLineItem情況下,我建議範圍轉換成一個方法

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

現在你會得到DiscountLineItem實例的集合。

+0

這應該是'def self.discount' – 2012-07-09 00:45:07

+1

我喜歡這個概念,但是使用map會將你的ActiveRecord :: Relation對象轉換爲一個數組,因此會殺死你鏈接任何其他作用域或方法的能力。必須有一種方法來乾淨地做到這一點:/ – 2012-07-09 00:46:24

+0

是的,我只需要閱讀關於將其轉換回關係的文檔。 – deefour 2012-07-09 00:49:25