控制轨道validation的顺序

我有一个轨道模型,有7个数字属性由用户通过表单填写。

我需要validation这些显然易于使用的每个属性的存在

validates :attribute1, :presence => true validates :attribute2, :presence => true # and so on through the attributes 

不过,我也需要运行一个自定义的validation器,它需要一些属性,并与他们做一些计算。 如果这些计算的结果不在一定范围内,那么该模型应该被宣布为无效。

就它自己而言,这也很容易

 validate :calculations_ok? def calculations_ok? errors[:base] << "Not within required range" unless within_required_range? end def within_required_range? # check the calculations and return true or false here end 

然而,问题是方法“validation”总是在方法“validation”之前运行。 这意味着如果用户将其中一个必填字段留空,则rails在尝试使用空属性执行计算时会引发错误。

那么我怎样才能首先检查所有必需属性的存在?

我不能确定这些validation是以什么顺序运行的,因为这可能取决于attributes哈希本身如何sorting。 你可能会更好地使你的validate方法更具弹性,如果缺less一些所需的数据,就不要运行。 例如:

 def within_required_range? return if ([ a, b, c, d ].find(&:blank?)) # ... end 

如果任何variablesad都是空白的,这将包含nil,空数组或string等等。

对于稍微复杂的情况,另一种方法是创build一个辅助方法,首先运行依赖属性的validation。 那么你可以使你的:calculate_ok? validation有条件地运行。

 validates :attribute1, :presence => true validates :attribute2, :presence => true ... validates :attribute7, :presence => true validate :calculations_ok?, :unless => Proc.new { |a| a.dependent_attributes_valid? } def dependent_attributes_valid? [:attribute1, ..., :attribute7].each do |field| self.class.validators_on(field).each { |v| v.validate(self) } return false if self.errors.messages[field].present? end return true end 

我不得不为项目创build类似的东西,因为依赖属性的validation非常复杂。 我相当于:calculate_ok? 如果依赖属性没有正确validation,则会抛出exception。

优点:

  • 相对干,特别是如果你的validation是复杂的
  • 确保您的错误数组报告正确的失败validation,而不是macrosvalidation
  • 会自动在您稍后添加的相关属性上包含任何其他validation

注意事项:

  • 潜在地运行所有validation两次
  • 您可能不希望所有validation在依赖属性上运行

看看http://railscasts.com/episodes/211-validations-in-rails-3

在实现一个自定义的validation器后,你只需要做

 validates :attribute1, :calculations_ok => true 

这应该可以解决你的问题。

James H解决scheme对我来说是最有意义的。 另一件需要考虑的事情是,如果你对依赖validation有条件,那么还需要检查depen_attributes_valid? 打电话工作。

即。

  validates :attribute1, presence: true validates :attribute1, uniqueness: true, if: :attribute1? validates :attribute1, numericality: true, unless: Proc.new {|r| r.attribute1.index("@") } validates :attribute2, presence: true ... validates :attribute7, presence: true validate :calculations_ok?, unless: Proc.new { |a| a.dependent_attributes_valid? } def dependent_attributes_valid? [:attribute1, ..., :attribute7].each do |field| self.class.validators_on(field).each do |v| # Surely there is a better way with rails? existing_error = v.attributes.select{|a| self.errors[a].present? }.present? if_condition = v.options[:if] validation_if_condition_passes = if_condition.blank? validation_if_condition_passes ||= if_condition.class == Proc ? if_condition.call(self) : !!self.send(if_condition) unless_condition = v.options[:unless] validation_unless_condition_passes = unless_condition.blank? validation_unless_condition_passes ||= unless_condition.class == Proc ? unless_condition.call(self) : !!self.send(unless_condition) if !existing_error and validation_if_condition_passes and validation_unless_condition_passes v.validate(self) end end return false if self.errors.messages[field].present? end return true end