2

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

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。我也尝试使用代码块,但也无法让它工作。我猜部分问题是记录尚未完全创建,但我找不到有关该问题的任何信息。

这种基于相关记录值的验证应该如何进行?

4

2 回答 2

3

通过添加您自己的验证方法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

于 2013-10-28T08:15:15.723 回答
0

正如pduersteler 所说,您可以使用验证方法。这是针对您的问题的问题:

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的 s

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

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

这仍然可能失败的一种方法是竞争条件。在创建出价的同时更改押金金额。验证Deposit在投标完成创建之前检查投标,因此它不被考虑在内,但它正在创建过程中,并且它的验证在它被更改之前Bid检查金额。Deposit两个验证均通过,然后创建出价并将存款金额更改为低于新出价的金额,并创建无效状态,没有错误或警告。

为避免这种情况,您需要在deposits创建或更新投标时锁定表中的相关记录,相反,您需要在bids更新存款时锁定表中的所有相关记录。

于 2019-08-30T11:42:36.760 回答