2011-08-26 38 views
2

我有一個虛擬的屬性,需要從表單字段一個時間範圍,將其分解:錯誤不會阻止對象保存?

def time_range=(time_range) 
    unless time_range.empty? 
    t = time_range.split(/to|\-/) 
    self.start_entry = t[0] 
    self.finish_entry = t[1] 
    if Chronic.parse(self.start_entry).nil? || Chronic.parse(self.finish_entry).nil? 
     errors.add(:time_range, 'Invalid time range entered') 
    end 
    end 
end 

start_entryfinish_entry也是因爲我有其他的方式來設置他們的虛擬屬性。無論兩人如何設置,我有以下鉤設置startfinish在我的數據庫:

before_save :set_start_and_finish

儘管我添加了錯誤事實,錯誤的對象仍設法節省:

> t = Tour.new 
> t.time_range = "rubbish" 
> t.errors 
#=> {:time_range=>["Invalid time range entered"]} 
> t.valid? 
#=> true 

如何使實例無效以防止以後保存?

回答

13

調用t.valid?運行驗證所以裏面time_range=您的驗證被忽略之前將清除錯誤。

如果我們看一下ActiveRecords's valid?,我們看到:

def valid?(context = nil) 
    context ||= (new_record? ? :create : :update) 
    output = super(context) 
    #... 

並且,超級應該送你到ActiveModel's valid?它開始像這樣:

def valid?(context = nil) 
    current_context, self.validation_context = validation_context, context 
    errors.clear 
    #... 

clear呼叫核武器的錯誤,你加入在time_range=

如果您想驗證某些內容,請使用驗證器。如果您想防止分配無效,請提出ArgumentError(或其他更合適的例外情況)。

驗證系統在運行驗證之前自行重置(即errors.clear)確實有意義。如果沒有重置,則必須丟棄並重新加載無效對象(或手動重置)以糾正驗證錯誤。僅僅因爲「更新,驗證,保存或銷燬」是Web應用程序的一般工作流程,並不意味着它是數據庫支持應用程序的唯一可能工作流程。

+0

有見地,謝謝!提出例外是一個很好的解決方案。 – Acco

1

set_start_and_finish看起來像是一個奇怪的地方要檢查驗證錯誤,但如果您檢測到錯誤以阻止其他回調執行,請確保您返回false

Read the section on "Cancelling callbacks"

+0

我知道這有點奇怪,但似乎在setter中驗證'time_range'是最好的,因爲它設置了共享虛擬屬性。 如果發現任何錯誤,我嘗試讓我的回調返回'false',但是這在'set_start_and_finish'內不起作用:'如果self.errors.presence'錯誤。錯誤的方法? – Acco

+0

這是什麼版本的Rails,你可以發佈真實的代碼嗎?另外,你最後一個例子中缺少一些東西,因爲在那裏沒有「保存」的調用。 – jdl

0

嘗試在驗證調用set_start_and_finish而不是before_save
作爲 validate :set_start_and_finish