0

假設我有兩個ActiveRecord模型:LineItem和Article。通過_id屬性更新AR關聯不會更新關聯對象(驗證前)

class LineItem < ActiveRecord::Base 
    belongs_to :article 
    ... 
end 

我遇到上了LineItem(Rails的2.3.11)以下行爲:

>> l = LineItem.new 
=> #<LineItem id: nil, article_id: nil, ...> 
>> l.article_id=10 
=> 10 
>> l.article 
=> #<Article id: 10, ...> 
>> l.article_id=20 
=> 20 
>> l.article 
=> #<Article id: 10, ...> 

所以,如果article_id的已經有值,隨後的變化不會改變文章的關聯了。 (至少不是立即 - 只有在保存之後,它才被設置爲新值。)

這在更新現有LineItems時導致我的validate方法出現問題。在我的了LineItem - 控制器我不更新這樣的:

def update 
    @line_item = LineItem.find(params[:id]) 
    @line_item.attributes = params[:data] #params[:data] contains article_id 
    ...  
    @line_item.save! 
    ... 
end 

在我LineItem類我有很多的驗證像這樣(簡化):

def validate 
    if self.article.max_size < self.size 
    errors.add_to_base("Too big for chosen article.") 
    end 
end 

在更新這個驗證作用於「老」的文章因爲在這一點上,新的只在self.article_id中(但不在self.article中)。在上述條件下,我可以用Article.find(self.article_id)代替self.article,但這看起來不像它的意思。

這是一個在rails(2.3.11)中的錯誤還是我做錯了什麼?非常感謝。

回答

0

這不是你遇到的錯誤,而是AR關聯緩存的行爲。通過將true傳遞給關聯方法:self.article(true),可以強制關聯在驗證過程中重新加載。

您也可以通過調用#clear_association_cache來清除LineItem實例的所有緩存關聯。

UPDATE:

它實際上是一個錯誤,但它已被固定! (見亞歷克斯的評論)

+0

謝謝,這是訣竅。備註:雖然傳遞true關聯始終有效,但self.clear_association_cache在上面的irb示例中不起作用,因爲它不作用於新的(未保存的)記錄。但在before_validation回調中使用它通常應該沒問題。 – MichMech

+0

這實際上是一個錯誤,一個非常可怕的錯誤,在互聯網上造成各種各樣的破壞。它固定在3.1或3.2中,但我經常遇到3.0和一些較早版本。如果在_id更改之前引用關聯(在模型過濾器中不常見),則關聯不會更改。 –

+0

@Alex謝謝,我更新了 –