341

我正在开发一个非常基本的购物车系统。

我有一个表items,其中有一列pricetype integer

对于包含欧元和美分的价格,我无法在我的视图中显示价格值。就在 Rails 框架中处理货币而言,我是否遗漏了一些明显的东西?

4

13 回答 13

511

您可能希望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任何地方手动转换为 s 和从 s 转换,这可能会变得很痛苦。

正如 mcl 所指出的,要打印价格,请使用:

number_to_currency(price, :unit => "€")
#=> €1,234.01
于 2009-06-19T20:48:57.167 回答
119

这是一种利用composed_of(ActiveRecord 的一部分,使用 ValueObject 模式)和 Money gem的精细、简单的方法

你需要

  • 金钱宝石(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显示货币格式正确的字符串
  • 如果您需要发送美分(到需要便士的支付网关),product.price.cents.to_s
  • 货币兑换免费
于 2010-10-02T00:15:14.620 回答
28

处理货币的常见做法是使用十进制类型。这是“使用 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 

理智地检查你的价值观。

于 2009-06-19T20:48:39.260 回答
15

对于一些有抱负的 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: 8scale: 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}'

正如这篇文中所解释的那样。

货币格式

告别额外的库并使用内置帮助程序。用作@molfnumber_to_currency和@facundofarias 的建议。

要在 Rails 控制台中使用helper,请向's类number_to_currency发送调用以访问 helper。ActiveSupportNumberHelper

例如:

ActiveSupport::NumberHelper.number_to_currency(2_500_000.61, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")

给出以下输出

2500000,61€

检查number_to_currency助手的另一个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选项,用于表示退款)

def refund_information
  amount_formatted = 
    ActionController::Base.helpers.number_to_currency(@refund.amount, negative_format: '(%u%n)')
  {
    # ...
    amount_formatted: amount_formatted,
    # ...
  }
end
于 2018-09-26T00:05:08.590 回答
12

如果您使用的是 Postgres(因为我们现在是 2017 年),您可能想:money尝试一下他们的列类型。

add_column :products, :price, :money, default: 0
于 2017-02-02T16:35:26.003 回答
9

使用money-rails gem。它可以很好地处理您模型中的货币和货币,并且还有一堆帮手来格式化您的价格。

于 2014-12-30T00:38:26.910 回答
5

使用虚拟属性(链接到修订(付费)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:虚拟属性虚拟属性是一种添加不直接映射到数据库的表单字段的干净方法。在这里,我将展示如何处理验证、关联等。

于 2012-09-22T21:08:45.360 回答
2

绝对是整数

即使 BigDecimal 在技术上存在1.5,仍然会在 Ruby 中为您提供纯浮点数。

于 2012-09-06T09:45:11.663 回答
2

如果有人使用 Sequel,则迁移看起来像:

add_column :products, :price, "decimal(8,2)"

不知何故,续集忽略了 :precision 和 :scale

(续集版本:sequel (3.39.0, 3.38.0))

于 2012-10-17T10:35:51.260 回答
2

我的底层 API 都使用美分来表示金钱,我不想改变这一点。我也没有用大量资金工作。所以我只是把它放在一个辅助方法中:

sprintf("%03d", amount).insert(-3, ".")

这会将整数转换为至少包含三位数字的字符串(必要时添加前导零),然后在最后两位数字之前插入一个小数点,从不使用Float. 从那里您可以添加适合您的用例的任何货币符号。

肯定又快又脏,但有时这很好!

于 2015-02-10T01:42:20.650 回答
2

我以这种方式使用它:

number_to_currency(amount, unit: '€', precision: 2, format: "%u %n")

当然,货币符号、精度、格式等取决于每种货币。

于 2015-02-17T17:34:18.083 回答
1

您可以将一些选项传递给number_to_currency(标准 Rails 4 视图助手):

number_to_currency(12.0, :precision => 2)
# => "$12.00"

Dylan Markow发布

于 2014-08-23T05:31:53.040 回答
0

Ruby & Rails 的简单代码

<%= number_to_currency(1234567890.50) %>

OUT PUT => $1,234,567,890.50
于 2017-03-30T08:37:51.400 回答