What is the best method of handling currency/money?
我正在研究一个非常基本的购物车系统。
我有一个表
我无法在视图中显示包含欧元和美分的价格的价格值。在Rails框架中处理货币方面,我是否遗漏了一些明显的东西?
您可能需要在数据库中使用
1 2 3 | # 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中,
如果您坚持使用整数,则必须在任何地方手动转换到和从
如MCL所指出,要打印价格,请使用:
1 2 | number_to_currency(price, :unit =>"€") #=> €1,234.01 |
这里有一个很好的、简单的方法,它利用了
你需要
- 货币宝石(4.1.0版)
- 一种模型,例如
Product 。 - 模型(和数据库)中的
integer 列,例如:price
把这个写在你的
1 2 3 4 5 6 7 | class Product > ActiveRecord::Base composed_of :price, :class_name => 'Money', :mapping => %w(price cents), :converter => Proc.new { |value| Money.new(value) } # ... |
你将得到:
- 如果没有任何额外的更改,您的所有表单都将显示美元和美分,但内部表示仍然只是美分。表单将接受类似于"12034.95美元"的值,并为您转换它。不需要向模型或视图中的助手添加额外的处理程序或属性。
product.price ="$12.00" 自动转换为货币类product.price.to_s 显示十进制格式的数字("1234.00")。product.price.format 为货币显示格式正确的字符串。- 如果您需要发送分(到需要分的支付网关),
product.price.cents.to_s 。 - 免费货币兑换
处理货币的常见做法是使用十进制类型。下面是一个简单的例子,"使用Rails的敏捷Web开发"
1 | add_column :products, :price, :decimal, :precision => 8, :scale => 2 |
这将允许您处理从-999999.99到999999.99的价格。您可能还希望在项目中包括验证,如
1 2 3 | def validate errors.add(:price,"should be at least 0.01") if price.nil? || price < 0.01 end |
为了理智,检查你的价值观。
使用金钱轨道宝石。它可以很好地处理您的模型中的货币和货币,并且有许多帮助者来格式化您的价格。
如果您正在使用Postgres(既然我们现在在2017年),那么您可能会尝试使用他们的
1 | add_column :products, :price, :money, default: 0 |
使用虚拟属性(链接到修订(付费)RailsCast),您可以在整型列中以美分为单位存储价格,并在产品模型中添加以美元为单位的虚拟属性价格作为getter和setter。
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 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 |
来源:railscapsts 016:虚拟属性:虚拟属性是一种干净的方式,可以添加不直接映射到数据库的表单字段。在这里,我将演示如何处理验证、关联等。
我使用它的方式是:
1 | number_to_currency(amount, unit: '€', precision: 2, format:"%u %n") |
当然,货币符号、精度、格式等都取决于每种货币。
如果有人正在使用Sequel,那么迁移看起来就像:
1 | add_column :products, :price,"decimal(8,2)" |
不知怎的,续集忽略了:精度和:尺度
(续集版本:续集(3.39.0,3.38.0))
绝对是整数。
即使bigdecimal技术上存在,
我的底层API都使用美分来表示钱,我不想改变这一点。我也没有用大量的钱工作。所以我把它放在一个助手方法中:
1 | sprintf("%03d", amount).insert(-3,".") |
它将整数转换为至少有三位数字的字符串(如有必要,可添加前导零),然后在最后两位数字之前插入小数点,而不使用
它肯定又快又脏,但有时也很好!
您可以将一些选项传递给
1 2 | number_to_currency(12.0, :precision => 2) # =>"$12.00" |
正如Dylan Markow发布的
对于一些有抱负的RoR开发中的初级/初级学生来说,这只是一个小小的更新和所有答案的内聚,肯定会有一些解释。
用钱工作按照@molf的建议,使用
1 2 3 | # 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 |
几点:
:decimal 将作为BigDecimal 使用,解决了很多问题。precision 和scale 应根据您所代表的内容进行调整。如果你在收付工作中,
precision: 8 和scale: 2 给予你最高的金额,在90%的情况下罚款。如果你需要代表一个财产或稀有汽车的价值,你应该使用更高的
precision 。如果你使用坐标(经度和纬度),你肯定需要一个更高的
scale 。
如何生成迁移
要使用上述内容生成迁移,请在终端中运行:
1 | bin/rails g migration AddPriceToItems price:decimal{8-2} |
或
1 | bin/rails g migration AddPriceToItems 'price:decimal{5,2}' |
如本文所述。
货币格式与额外的库吻别,使用内置的帮助器。使用
要在Rails控制台中使用
例如:
1 | ActiveSupport::NumberHelper.number_to_currency(2_500_000.61, unit: '€', precision: 2, separator: ',', delimiter: '', format:"%n%u") |
给出以下输出
1 | 2500000,61€ |
检查另一个
您可以将它放在应用程序帮助程序中,并在视图中以任意数量使用它。
1 2 3 4 5 | module ApplicationHelper def format_currency(amount) number_to_currency(amount, unit: '€', precision: 2, separator: ',', delimiter: '', format:"%n%u") end end |
或者您可以将它作为一个实例方法放在
1 2 3 4 5 | class Item < ActiveRecord::Base def format_price number_to_currency(price, unit: '€', precision: 2, separator: ',', delimiter: '', format:"%n%u") end end |
还有一个例子,我如何在控制者内部使用
1 2 3 4 5 6 7 8 9 | def refund_information amount_formatted = ActionController::Base.helpers.number_to_currency(@refund.amount, negative_format: '(%u%n)') { # ... amount_formatted: amount_formatted, # ... } end |
Ruby&Rails的简单代码
1 2 3 | <%= number_to_currency(1234567890.50) %> OUT PUT => $1,234,567,890.50 |