我的模型有一个小数数量属性.

create_table :foos do |t|
  t.decimal :amount
end

class Foo < ApplicationRecord
end

我总是希望金额为负值,所以我添加了一个正常化:

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

现在,当我将金额设置为正值时,尽管标准化会将其转换为负值,但验证器似乎认为该值仍然是正值,并添加了验证错误.

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}>

根据normalizesnormalisation happens before validation.

我怎么才能让它工作?

推荐答案

Numericality validator seems to be specifically using raw value for validation without taking normalization into account:
https://github.com/rails/rails/blob/v7.1.3/activemodel/lib/active_model/validations/numericality.rb#L129

if record.respond_to?(came_from_user)
  if record.public_send(came_from_user)
    raw_value = record.public_send(:"#{attr_name}_before_type_cast")

它需要以这种方式进行,因为字符串归一化为数字("foo".to_d # => 0.0),因此如果在归一化之后进行验证,则验证将不起作用.

您可以编写自己的验证来绕过此问题:

validate do
  errors.add(:amount, :less_than, value: amount, count: 0) unless amount.negative?
end

Ruby-on-rails相关问答推荐

在VP中部署rails 7和nginx

在控制器操作中适当使用ActiveModel Dirty

包含字母和数字组合的正则表达式

关系 fields_for 上不允许的参数

如何在 Ruby on Rails 中正确存储 BigDecimal

Rails 控制台 - 更新 Gem 后的弃用通知

无法加载此类文件 -- mysql2/mysql2

Selenium chromedriver:无法从不可键入的键构造KeyEvent

Rails 4 中的 null_session 和 reset_session 有什么区别?

如何添加到序列化数组

FactoryGirl + Faker - 为数据库种子数据中的每个对象生成相同的数据

Rails ActiveRecord 查询日期范围

没有要加载的文件 - readline

Rails - 生产模式下的错误

在 Rails/ActiveRecord 列名中使用问号字符

在 ruby​​ 中构建公钥时,是什么导致既不是 PUB key 也不是 PRIV key::nested asn1 错误?

在 Rails 应用程序中处理大文件上传的最佳方法是什么?

如何检测我的代码是否在 Rails 3 的控制台中运行?

Ruby 1.87 与 1.92 Date.parse

没有路由匹配缺少必需的键:[:id]