错误不会阻止对象保存?

acc*_*cco 3 ruby ruby-on-rails

我有一个虚拟属性,它从表单字段中获取时间范围并将其拆分:

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
Run Code Online (Sandbox Code Playgroud)

start_entry并且finish_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
Run Code Online (Sandbox Code Playgroud)

如何使实例无效以防止以后保存?

mu *_*ort 14

调用t.valid?将在运行验证之前清除错误,以便time_range=忽略内部验证.

如果我们看一下ActiveRecordsvalid?,我们会看到:

def valid?(context = nil)
  context ||= (new_record? ? :create : :update)
  output = super(context)
  #...
Run Code Online (Sandbox Code Playgroud)

超级应该把你送到ActiveModelvalid?,它开始像这样:

def valid?(context = nil)
  current_context, self.validation_context = validation_context, context
  errors.clear
  #...
Run Code Online (Sandbox Code Playgroud)

并且clear呼叫核对你添加的错误time_range=.

如果要验证某些内容,请使用验证器.如果要防止无效赋值,请引发ArgumentError(或其他更合适的异常).

让验证系统errors.clear在运行验证之前自行重置(即)确实有意义.如果它没有重置,你必须扔掉并重新加载一个无效的对象(或手动重置它)只是为了纠正验证错误.仅仅因为"更新,验证,保存或销毁"是Web应用程序的一般工作流程并不意味着它是数据库支持的应用程序唯一可能的工作流程.