Rails根据相关记录进行验证

时间:2013-10-28 08:11:31

标签: ruby-on-rails validation

我有两个类,存款和出价,它们关联为:

class Bid < ActiveRecord::Base 
   belongs_to :deposit
end

class Deposit < ActiveRecord::Base
   has_many :bids
end

沉积物具有固定的量,其在创建时设定。出价也有金额,我正在尝试使用验证来确保bid.amount小于它所属的存款金额。

我尝试通过以下方式实现这一目标:

class Bid < ActiveRecord::Base
    validates :amount, numericality: { only_integer: true, less_than_or_equal_to: self.deposit.amount }
end

但它给了我NoMethodError。我也尝试过使用代码块,但也无法使用它。我猜测问题的一部分是记录尚未完全创建,但我找不到有关该问题的任何信息。

如何根据相关记录值进行此类验证?

2 个答案:

答案 0 :(得分:2)

通过validate添加您自己的验证方法(注意:单数)。

class Bid < ActionRecord::Base
  validate :my_thing

  def my_thing
    unless self.my_condition
      errors.add :field, :message
    end
  end
end

请参阅http://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations

答案 1 :(得分:0)

作为pduersteler says,您可以使用验证方法。这是特定于您的问题的一个:

class Bid < ActionRecord::Base
  validates :amount, numericality: { only_integer: true }
  validate :amount_not_more_than_deposit_amount

  def amount_not_more_than_deposit_amount
    if amount > deposit.amount
      errors.add :amount, "cannot be greater than the deposit amount"
    end
  end

end

但是,当您基于关联对象中的值验证对象时,必须考虑如果该关联模型被编辑会发生什么情况。在这种情况下,投标金额永远不应大于关联的保证金金额,但是如果已经附加投标的保证金被编辑为较低金额,会发生什么情况。 ActiveRecord将允许此操作而不会发出任何警告或错误,因此,在验证Deposit时要防止这种情况,您还应该验证所有关联的Bid

class Deposit < ActiveRecord::Base
   has_many :bids
   validate_associated :bids
end

这样,将某笔存款的金额设置为低于其中一个出价的存款将无效。这将为应用程序带来一些额外的健壮性。

这仍然可能失败的一种方法是在竞争条件下。在创建投标的同时更改存款金额。对Deposit的验证会在出价创建完毕之前检查出价,因此不会对其进行考虑,但是Bid正在创建过程中,其验证会检查{{1 }},然后再进行更改。两次验证均通过,然后创建了出价,并且存款金额更改为低于新出价的金额,并且创建了无效状态,没有任何错误或警告。

为避免这种情况,您需要在创建或更新出价时锁定Deposit表中相关记录,相反,您需要锁定{{ 1}}表,而正在更新存款。