我有一个虚拟属性,它从表单字段中获取时间范围并将其拆分:
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_entry
和finish_entry
也是虚拟属性,因为我有其他方法来设置它们。无论两者是如何设置的,我都有以下钩子在我的数据库中设置start
和finish
:
before_save :set_start_and_finish
尽管我添加了错误,但错误的对象仍设法保存:
> t = Tour.new
> t.time_range = "rubbish"
> t.errors
#=> {:time_range=>["Invalid time range entered"]}
> t.valid?
#=> true
如何使实例无效以防止以后保存?
答案 0 :(得分:14)
调用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应用程序的一般工作流程并不意味着它是数据库支持的应用程序唯一可能的工作流程。
答案 1 :(得分:1)
set_start_and_finish
似乎是一个检查验证错误的奇怪地方,但如果检测到错误以阻止其余回调执行,请确保返回false
。
答案 2 :(得分:0)
尝试在验证时调用set_start_and_finish
而不是before_save
如
validate :set_start_and_finish