18

允许用户在将数字输入表单时使用小数或逗号的最佳 Ruby/Rails 方法是什么?换句话说,我希望用户能够输入 2,000.99 而不会在我的数据库中获得 2.00。

对此有最佳做法吗?

- 更新 - -

gsub 是否适用于浮点数或大整数?或者,在将浮点数或整数输入表单时,rails 会自动切断 , 处的数字吗?我尝试使用 self.price.gsub(",", "") 但得到“undefined method `gsub' for 8:Fixnum”,其中 8 是我在表单中输入的任何数字。

4

8 回答 8

27

我在尝试在表单中使用本地化内容时遇到了类似的问题。ActionView::Helpers::NumberHelper使用内置方法本地化输出相对简单,但不支持解析本地化输入ActiveRecord

这是我的解决方案,请告诉我我是否做错了什么。在我看来,这太简单了,无法成为正确的解决方案。谢谢!:)

首先,让我们添加一个方法到String.

class String
  def to_delocalized_decimal
    delimiter = I18n::t('number.format.delimiter')
    separator = I18n::t('number.format.separator')
    self.gsub(/[#{delimiter}#{separator}]/, delimiter => '', separator => '.')
  end
end

然后让我们添加一个类方法ActiveRecord::Base

class ActiveRecord::Base
  def self.attr_localized(*fields)
    fields.each do |field|
      define_method("#{field}=") do |value|
        self[field] = value.is_a?(String) ? value.to_delocalized_decimal : value
      end
    end
  end
end

最后,让我们声明哪些字段应该本地化输入。

class Article < ActiveRecord::Base
  attr_localized :price
end

现在,在您的表单中,您可以输入“1.936,27”并且ActiveRecord不会在无效数字上引发错误,因为它变成了 1936.27。

于 2013-02-19T15:51:38.630 回答
10

这是几年前我从 Greg Brown(Ruby Best Practices的作者)那里复制的一些代码。在您的模型中,您确定哪些项目是“人性化的”。

class LineItem < ActiveRecord::Base
  humanized_integer_accessor :quantity
  humanized_money_accessor :price
end

在您的视图模板中,您需要引用人性化的字段:

= form_for @line_item do |f|
  Price:
  = f.text_field :price_humanized

这是由以下因素驱动的:

class ActiveRecord::Base
  def self.humanized_integer_accessor(*fields)
    fields.each do |f|
      define_method("#{f}_humanized") do
        val = read_attribute(f)
        val ? val.to_i.with_commas : nil
      end
      define_method("#{f}_humanized=") do |e|
        write_attribute(f,e.to_s.delete(","))
      end
    end
  end

  def self.humanized_float_accessor(*fields)
    fields.each do |f|
      define_method("#{f}_humanized") do
        val = read_attribute(f)
        val ? val.to_f.with_commas : nil
      end
      define_method("#{f}_humanized=") do |e|
        write_attribute(f,e.to_s.delete(","))
      end
    end
  end

  def self.humanized_money_accessor(*fields)
    fields.each do |f|
      define_method("#{f}_humanized") do
        val = read_attribute(f)
        val ? ("$" + val.to_f.with_commas) : nil
      end
      define_method("#{f}_humanized=") do |e|
        write_attribute(f,e.to_s.delete(",$"))
      end
    end
  end
end
于 2011-07-01T07:59:58.070 回答
2

您可以尝试去掉逗号 before_validation 或 before_save

糟糕,您想在转换之前在文本字段上执行此操作。您可以使用虚拟属性:

def price=(price)
   price = price.gsub(",", "")
   self[:price] = price  # or perhaps price.to_f
end
于 2011-06-30T21:32:53.810 回答
0

查看i18n_alchemy gem 进行日期和数字解析和本地化。

I18nAlchemy 旨在处理基于当前 I18n 语言环境格式的日期、时间和数字解析。主要思想是让 ORM(例如现在的 ActiveRecord)自动接受以当前语言环境格式给出的日期/数字,并返回这些本地化的值。

于 2016-09-07T09:06:16.163 回答
0

我在我的项目中编写了以下代码。这解决了我所有的问题。

config/initializers/decimal_with_comma.rb

# frozen_string_literal: true

module ActiveRecord
  module Type
    class Decimal
      private

      alias_method :cast_value_without_comma_separator, :cast_value

      def cast_value(value)
        value = value.gsub(',', '') if value.is_a?(::String)
        cast_value_without_comma_separator(value)
      end
    end

    class Float
      private

      alias_method :cast_value_without_comma_separator, :cast_value

      def cast_value(value)
        value = value.gsub(',', '') if value.is_a?(::String)
        cast_value_without_comma_separator(value)
      end
    end

    class Integer
      private

      alias_method :cast_value_without_comma_separator, :cast_value

      def cast_value(value)
        value = value.gsub(',', '') if value.is_a?(::String)
        cast_value_without_comma_separator(value)
      end
    end
  end
end

module ActiveModel
  module Validations
    class NumericalityValidator
      protected

        def parse_raw_value_as_a_number(raw_value)
          raw_value = raw_value.gsub(',', '') if raw_value.is_a?(::String)
          Kernel.Float(raw_value) if raw_value !~ /\A0[xX]/
        end
    end
  end
end
于 2019-10-31T06:24:46.017 回答
-1

我无法实现较早的def price=(price)虚拟属性建议,因为该方法似乎递归调用自身。

我最终从属性哈希中删除了逗号,因为您怀疑 ActiveRecord 似乎用逗号截断输入,这些逗号被插入 DECIMAL 字段。

在我的模型中:

before_validation :remove_comma

def remove_comma
  @attributes["current_balance"].gsub!(',', '')  # current_balance here corresponds to the text field input in the form view

  logger.debug "WAS COMMA REMOVED? ==> #{self.current_balance}"
end
于 2011-07-24T05:43:02.667 回答
-1

这是确保正确读取数字输入的简单方法。输出仍然是一个点而不是逗号。这并不漂亮,但至少在某些情况下并不重要。

它需要在要启用逗号分隔符的控制器中调用一个方法。在 MVC 方面可能并不完美,但非常简单,例如:

class ProductsController < ApplicationController

  def create
    # correct the comma separation:
    allow_comma(params[:product][:gross_price])

    @product = Product.new(params[:product])

    if @product.save
      redirect_to @product, :notice => 'Product was successfully created.'
    else
      render :action => "new"
    end
  end

end

想法是修改参数字符串,例如:

class ApplicationController < ActionController::Base

  def allow_comma(number_string)
    number_string.sub!(".", "").sub!(",", ".")
  end

end
于 2012-03-20T10:49:45.477 回答
-1

你可以试试这个:

def price=(val)
  val = val.gsub(',', '')
  super
end
于 2019-10-30T03:57:09.980 回答