You need to enable JavaScript to run this app. Ruby on Rails 中如何实现 decimal 类型价格的最多两位小数校验
优惠活动
大模型
产品
解决方案
定价
更多

Ruby on Rails 中如何实现 decimal 类型价格的最多两位小数校验

问题原因
  • 数据库自动截断精度:如果迁移中给price字段设置了scale: 2(最多保留两位小数),Rails赋值时会自动对超出位数的小数做四舍五入,比如输入14.995会被自动转为15.0,校验拿到的已经是处理后的值,无法触发校验失败。
  • 类型不匹配:正则校验针对字符串生效,而price作为decimal类型实际是BigDecimal对象,大数值转成字符串时会以科学计数法表示,正则无法匹配这类格式,导致校验失效。
解决方案

前置调整

先修改数据库迁移,将price的小数位数上限放宽到大于2(比如5位),避免赋值时被自动四舍五入,让校验可以拿到原始输入的精度:

# db/migrate/xxxxxx_create_logos.rb
t.decimal :price, precision: 10, scale: 5

模型层自定义校验

通过「乘以100后是否为整数」的逻辑判断小数位数,避免正则匹配和浮点数精度问题:

class Logo < ApplicationRecord
  validates :name, presence: true, length: { maximum: 255 }
  validates :price, presence: true, numericality: { greater_than_or_equal_to: 0 }
  validates :stitch_count, presence: true, numericality: { greater_than: 0, only_integer: true }

  validate :price_must_have_at_most_two_decimal_places

  private

  def price_must_have_at_most_two_decimal_places
    return if price.nil?
    # 统一转成BigDecimal处理,避免浮点数精度误差
    decimal_price = BigDecimal(price.to_s)
    # 乘以100后若存在小数部分,说明原始值超过两位小数
    unless (decimal_price * 100).modulo(1).zero?
      errors.add(:price, "最多只能保留两位小数")
    end
  end
end

校验效果

  • 合规值10.44、12.1、16、14.00可正常通过
  • 非合规值10.123、13.11111、14.995会触发校验错误
测试适配

原有测试用例无需修改,完成上述调整后即可正常运行:

def setup
  @logo = Logo.new(name: "ASG Security", price: 12, stitch_count: 15000, note: "Ali")
end

test "price scale must be less than 2" do
  @logo.price = 14.995
  assert_not @logo.valid?
end

内容的提问来源于stack exchange,提问作者izaguirrejoe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.05 21:24:02