为何Rails数值验证器未使用归一化后的值?
问题:Rails属性归一化后验证仍使用原始值
我的模型有一个decimal类型的amount属性:
create_table :foos do |t| t.decimal :amount end class Foo < ApplicationRecord end
为了让amount始终为负值,我添加了归一化处理:
class Foo < ApplicationRecord normalizes :amount, with: -> amount { - amount.abs } end
归一化功能本身运行正常,但添加验证规则后出现问题:
class Foo < ApplicationRecord normalizes :amount, with: -> amount { - amount.abs } validates :amount, numericality: {less_than: 0} end
当设置amount为正值时,虽然归一化已经将其转为负值,但验证器仍使用原始值判断,导致验证失败:
foo = Foo.new amount: 4 foo.amount # => -4 foo.valid? # => false foo.errors # => #<ActiveModel::Error attribute=amount, type=less_than, options={:value=>4, :count=>0}>
按normalizes的设计逻辑,归一化应在验证之前执行,但实际验证却沿用了原始输入值。
解决方案
这是因为Rails默认验证会使用原始属性值校验,而非归一化后的值。以下两种方案可解决该问题:
方案1:自定义验证逻辑,使用归一化后的值
手动调用normalized_value_for方法获取处理后的属性值,编写自定义验证:
class Foo < ApplicationRecord normalizes :amount, with: -> amount { - amount.abs } validate do errors.add(:amount, "必须小于0") if normalized_value_for(:amount) >= 0 end end
方案2:指定验证器使用归一化值(Rails 7.1+)
Rails 7.1及以上版本支持在验证器中通过value: :normalized参数,强制使用归一化后的属性值进行校验:
class Foo < ApplicationRecord normalizes :amount, with: -> amount { - amount.abs } validates :amount, numericality: {less_than: 0, value: :normalized} end
内容的提问来源于stack exchange,提问作者Andy Stewart
相关产品推荐
相关产品推荐

