假设这样的架构:
create_table "bills", :force => true do |t|
t.decimal "cost", :precision => 10, :scale => 5
end
我想编写一个函数,如果它是唯一的,就会将新帐单写入数据库。以下不起作用:
def load_bill_unless_exists(candidate)
incumbents = Bill.scoped.where(:cost => candidate.cost)
candidate.save unless incumbents.exists?
end
因为现任账单和候选账单的BigDecimal表示具有不同的限制,因此:cost => candidate.cost
测试失败。也就是说,它正在比较:
candidate: #<Bill id: nil, cost: #<BigDecimal:105e39850,'0.1670576666 6666666E4',27(27)>>
与
incumbent: #<ServiceBill id: 198449, cost: #<BigDecimal:105e35840,'0.167057667E4',18(18)>>
请注意,候选人的BigDecimal表示比现任者更多的数字。
所以问题很简单:执行此比较的正确方法是什么?我考虑过:cost => BigDecimal.new(candidate.cost.to_s, 18)
,但这感觉不对 - 例如,18号来自哪里?
答案 0 :(得分:1)
如果像你正在考虑作品一样,你可能不得不接受它。您正在使用的查询只是构建“WHERE cost = number”,如果数据库无法与传递的数字正确比较,则需要以不同方式传递它。看起来这是数据库阻止你而不是Rails中的任何东西。
如果您不喜欢在查询中进行投射,可以随时在模型中执行:
def cost_with_incumbent_precision
BigDecimal.new(cost.to_s, 18)
end
答案 1 :(得分:1)
尝试使用BigDecimal#round
:
def load_bill_unless_exists(candidate)
incumbents = Bill.scoped.where(:cost => candidate.cost.round(5))
candidate.save unless incumbents.exists?
end
来自文档:
舍入到最接近的1(默认情况下),将结果作为BigDecimal返回。如果n指定为正数,则结果的小数部分不会超过该数字。
鉴于您已在模式中指定了5的精度,这是您在进行比较时应该舍入的内容。