处理货币/金钱的最佳方法是什么?
我正在研究一个非常基本的购物车系统。
我有一个表格的items
有一个integer
types的列price
。
在我看来,包括欧元和美分的价格中,我无法显示价格价值。 就我在Rails框架中处理货币而言,我是否错过了一些显而易见的东西?
您可能会想在数据库中使用DECIMAL
types。 在你的迁移中,做这样的事情:
# precision is the total number of digits # scale is the number of digits to the right of the decimal point add_column :items, :price, :decimal, :precision => 8, :scale => 2
在Rails中, :decimal
types返回为BigDecimal
,这对计算价格很有帮助。
如果你坚持使用整数,你将不得不手动转换BigDecimal
到任何地方,这可能只是一个痛苦。
正如mcl指出的那样,要打印价格,请使用:
number_to_currency(price, :unit => "€") #=> €1,234.01
这是一个很好的,简单的方法,它利用了composed_of
(ActiveRecord的一部分,使用ValueObject模式)和金钱gem
你需要
- Moneygem (版本4.1.0)
- 一个模型,例如
Product
- 模型(和数据库)中的
integer
列,例如:price
写在你的product.rb
文件中:
class Product > ActiveRecord::Base composed_of :price, :class_name => 'Money', :mapping => %w(price cents), :converter => Proc.new { |value| Money.new(value) } # ...
你会得到什么:
- 没有任何额外的变化,所有的表格将显示美元和美分,但内部表示仍然只是美分。 表单将接受像“$ 12,034.95”这样的值并将其转换为您。 不需要在模型中添加额外的处理程序或属性,或者在视图中添加辅助对象。
-
product.price = "$12.00"
自动转换为Money类 -
product.price.to_s
显示十进制格式的数字(“1234.00”) -
product.price.format
显示货币格式正确的string - 如果您需要发送美分(到想要便士的支付网关),请input
product.price.cents.to_s
- 货币兑换是免费的
处理货币的通常做法是使用十进制types。 下面是一个简单的例子,来自“使用Rails进行敏捷Web开发”
add_column :products, :price, :decimal, :precision => 8, :scale => 2
这将使您能够处理从-999,999.99到999,999.99的价格
你可能也想在你的项目中joinvalidation
def validate errors.add(:price, "should be at least 0.01") if price.nil? || price < 0.01 end
要理智,检查你的价值观。
使用钱轨gem 。 它很好地处理您的模型中的货币和货币,也有一大帮助人来格式化您的价格。
使用虚拟属性(链接到修改(付费)Railscast),您可以将price_in_cents存储在整数列中,并在您的产品模型中添加虚拟属性price_in_dollars作为getter和setter。
# Add a price_in_cents integer column $ rails g migration add_price_in_cents_to_products price_in_cents:integer # Use virtual attributes in your Product model # app/models/product.rb def price_in_dollars price_in_cents.to_d/100 if price_in_cents end def price_in_dollars=(dollars) self.price_in_cents = dollars.to_d*100 if dollars.present? end
来源: RailsCasts#016: 虚拟属性 : 虚拟属性是一种干净的方式来添加不直接映射到数据库的表单域。 在这里我展示了如何处理validation,关联等等。
如果您使用的是Postgres(因为我们现在是在2017年),您可能想要尝试使用他们的:money
列types。
add_column :products, :price, :money, default: 0
绝对是整数 。
即使BigDecimal在技术上存在1.5
仍然会给你一个纯粹的Ruby的Float。
如果有人使用续集,则迁移将如下所示:
add_column :products, :price, "decimal(8,2)"
不知何故,续集忽略:精确性和:规模
(续集版本:续集(3.39.0,3.38.0))
我正在使用这种方式:
number_to_currency(amount, unit: '€', precision: 2, format: "%u %n")
当然,货币符号,精度,格式等依赖于每种货币。
您可以将一些选项传递给number_to_currency
(一个标准的Rails 4视图助手):
number_to_currency(12.0, :precision => 2) # => "$12.00"
由Dylan Markow发布
我的底层API都是用美分来代表金钱的,我不想改变它。 我也没有用大笔的钱工作。 所以我只是把它放在一个辅助方法中:
sprintf("%03d", amount).insert(-3, ".")
将整数转换为一个至less包含三位数字的string(如果需要,加上前导零),然后在最后两位数字之前插入一个小数点,从不使用Float
。 从那里你可以添加任何货币符号适合你的用例。
这绝对是快速和肮脏的,但有时,这很好!
简单的Ruby和Rails代码
<%= number_to_currency(1234567890.50) %> OUT PUT => $1,234,567,890.50