2011-08-26 100 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