我正在开发一个非常基本的购物车系统。
我的表items
有一个price
类型的列integer
。
我无法在我的观点中显示包含欧元和美分价格的价格值。就Rails框架中的处理货币而言,我是否遗漏了一些明显的东西?
答案 0 :(得分:478)
您可能希望在数据库中使用DECIMAL
类型。在迁移中,请执行以下操作:
# 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
类型返回为BigDecimal
,这非常适合进行价格计算。
如果你坚持使用整数,你将不得不在任何地方手动转换为BigDecimal
,这可能只是一种痛苦。
正如mcl所指出的,要打印价格,请使用:
number_to_currency(price, :unit => "€")
#=> €1,234.01
答案 1 :(得分:112)
这是一个很好的,简单的方法,利用composed_of
(ActiveRecord的一部分,使用ValueObject模式)和Money gem
你需要
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) }
# ...
你会得到什么:
product.price = "$12.00"
会自动转换为Money类product.price.to_s
显示十进制格式的数字(“1234.00”)product.price.format
显示货币格式正确的字符串product.price.cents.to_s
答案 2 :(得分:25)
处理货币的常用做法是使用小数类型。 以下是“使用Rails进行敏捷Web开发”的简单示例
add_column :products, :price, :decimal, :precision => 8, :scale => 2
这将允许您处理从-999,999.99到999,999.99的价格 您可能还希望在项目中加入验证,例如
def validate
errors.add(:price, "should be at least 0.01") if price.nil? || price < 0.01
end
要理智 - 检查你的价值观。
答案 3 :(得分:7)
使用money-rails gem。它可以很好地处理模型中的货币和货币,并且还有一堆帮助器来格式化您的价格。
答案 4 :(得分:6)
如果您正在使用Postgres(现在我们已经在2017年),您可能需要尝试使用:money
列类型。
add_column :products, :price, :money, default: 0
答案 5 :(得分:5)
使用Virtual Attributes (Link to revised(paid) 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: Virtual Attributes:虚拟属性是添加不直接映射到数据库的表单字段的简洁方法。在这里,我将展示如何处理验证,关联等。
答案 6 :(得分:2)
绝对是integers。
即使BigDecimal在技术上存在,1.5
仍会在Ruby中为您提供纯粹的Float。
答案 7 :(得分:2)
如果有人使用续集,迁移将类似于:
add_column :products, :price, "decimal(8,2)"
不知怎的,Sequel忽略了:精度和:比例
(续集版本:续集(3.39.0,3.38.0))
答案 8 :(得分:2)
我正是以这种方式使用它:
number_to_currency(amount, unit: '€', precision: 2, format: "%u %n")
当然货币符号,精度,格式等取决于每种货币。
答案 9 :(得分:1)
您可以将一些选项传递给number_to_currency
(标准的Rails 4视图帮助程序):
number_to_currency(12.0, :precision => 2)
# => "$12.00"
发布
答案 10 :(得分:1)
我的底层API都使用美分代表金钱,我不想改变它。我也不是在处理大笔资金。所以我把它放在一个辅助方法中:
sprintf("%03d", amount).insert(-3, ".")
将整数转换为至少包含三位数的字符串(必要时添加前导零),然后在最后两位数之前插入一个小数点,从不使用Float
。从那里,您可以添加适合您的用例的任何货币符号。
它肯定快速而肮脏,但有时候那很好!
答案 11 :(得分:0)
Ruby&amp;的简单代码轨道强>
<%= number_to_currency(1234567890.50) %>
OUT PUT => $1,234,567,890.50
答案 12 :(得分:0)
对于RoR开发中一些有抱负的初级/初学者来说,只需稍作更新和全部答案,这些肯定会在这里进行一些解释。
按照{molf的建议,使用:decimal
将钱存储在数据库中(以及我公司在使用钱时用作黄金标准)。
# 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
会给您999,999.99
作为最高金额,在90%的情况下都可以。
如果您需要表示房地产或稀有汽车的价值,则应使用更高的precision
。
如果使用坐标(经度和纬度),则肯定需要更高的scale
。
要生成具有上述内容的迁移,请在终端中运行:
bin/rails g migration AddPriceToItems price:decimal{8-2}
或
bin/rails g migration AddPriceToItems 'price:decimal{5,2}'
如本blog帖子中所述。
KISS额外的库再见,并使用内置的帮助器。使用number_to_currency
,如@molf和@facundofarias建议使用。
要在Rails控制台中使用number_to_currency
帮助程序,请调用ActiveSupport
的{{1}}类以访问该帮助程序。
例如:
NumberHelper
给出以下输出
ActiveSupport::NumberHelper.number_to_currency(2_500_000.61, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
检查number_to_currency助手中的另一个2500000,61€
。
您可以将其放在应用程序助手中,并在视图内部使用任意数量。
options
或者您可以将其作为实例方法放在module ApplicationHelper
def format_currency(amount)
number_to_currency(amount, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
end
end
模型中,并在需要设置价格格式的位置(在视图或助手中)调用它。
Item
还有一个示例,说明如何在控制器中使用class Item < ActiveRecord::Base
def format_price
number_to_currency(price, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
end
end
(注意number_to_currency
选项,用于表示退款)
negative_format