基于Rails中的关联值的自定义验证器

时间:2015-03-22 14:16:51

标签: ruby-on-rails ruby-on-rails-4

我的应用程序有3个模型(Foo,Bar和Baz)

class Foo
  has_many :bars

class Bar
  belongs_to :foo
  has_many   :bazs

class Baz
  belongs_to :bar

我正在尝试找到最直观的方法来验证Baz(价格)中的属性,而不是在Foo(:min_price和:max_price)中设置的限制。 Baz模型中的价格必须介于Foo设定的限制之间。

目前我正在尝试使用自定义PriceLimitValidator实现它:

class PriceLimitValidator < ActiveModel::Validator
  def validate(record)
    unless ( record.price <= record.bar.foo.max_price && record.price >= record.bar.foo.min_price )
      record.errors[:price] << 'Price limit exceeded!'
    end
  end
end

这会导致NilClass错误,坦率地说,这并不是最简单的解决方案。

任何关于rails初学者的提示?

2 个答案:

答案 0 :(得分:1)

可能.bar或.bar.foo不存在。我会以这种方式实现它:

class Foo
  validates_presence_of :min_price, :max_price
end

class Baz
  belongs_to :bar
  has_one :foo, through: :bar

  validates_presence_of :foo

  def min_price
    foo.try(:min_price)
  end

  def max_price
    foo.try(:min_price)
  end
end

class PriceLimitValidator < ActiveModel::Validator
  def validate(record)
    return if record.min_price.nil? || record.max_price.nil?

    unless record.price.between?(record.min_price, record.max_price)
      record.errors[:price] << 'Price limit exceeded!' 
    end
  end
end

检查Baz和Foo之间的关系。它不能是belongs_to,因为belongs_to假设foreign_key存在于第一个模型中。在这种情况下,您必须定义has_one:through。

我也在Baz模型中实现了min_price和max_price。它是一个方便的模式,允许您缩短方法链。此外,它允许验证器仅检查此属性,而无需了解模型关联。

最重要的是我验证存在:foo和PriceLimitValidator我检查是否定义了min_price和max_price。检查相关对象是否存在,验证者不负责。

当然,您可以根据自己的需要调整逻辑。

答案 1 :(得分:1)

Rails提供基于Ruby范围功能的开箱即用的验证。

例如:

validates_numericality_of :width, less_than: ->(person) { person.height }, :greater_than... etc. } 

完整的规范及其无数选项位于:http://apidock.com/rails/ActiveModel/Validations/HelperMethods/validates_numericality_of