如何在Ruby/Rails中允许用户在表单中输入小数点或逗号?换句话说,我希望用户能够输入2,000.99而不是在我的数据库中得到2.00。
这方面是否有最佳实践?
gsub是否可以用于浮点数或大整数?或者当将浮点数或整数输入表单时,Rails会自动在逗号处截断数字吗?我尝试使用self.price.gsub(",", "")但得到"undefined method `gsub' for 8:Fixnum",其中8是我在表单中输入的任何数字。
如何在Ruby/Rails中允许用户在表单中输入小数点或逗号?换句话说,我希望用户能够输入2,000.99而不是在我的数据库中得到2.00。
这方面是否有最佳实践?
gsub是否可以用于浮点数或大整数?或者当将浮点数或整数输入表单时,Rails会自动在逗号处截断数字吗?我尝试使用self.price.gsub(",", "")但得到"undefined method `gsub' for 8:Fixnum",其中8是我在表单中输入的任何数字。
我曾遇到一个类似的问题,试图在表单中使用本地化内容。使用内置方法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。
这是我几年前从 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
with_commas
方法是什么? - Tinynumbersclass String def with_commas self.reverse.gsub(/\d{3}/,"\\&,").reverse.sub(/^,/,"") end class Numeric def with_commas to_s.with_commas end end
- Jeremy Weathersdef price=(price)
price = price.gsub(",", "")
self[:price] = price # or perhaps price.to_f
end
self.price = price
会调用 price=
。我建议改为使用 self[:price] = price
。 - epochwolf看看i18n_alchemy gem,用于日期和数字解析以及本地化。
I18nAlchemy旨在处理基于当前I18n区域设置格式的日期、时间和数字解析。主要思想是让ORM(例如ActiveRecord)自动接受以当前语言环境格式给出的日期/数字,并将这些值本地化。
我在我的项目中编写了以下代码。这解决了我所有的问题。
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
这里有一个简单的方法,可以确保数字输入被正确读取。输出仍然会带有点而不是逗号。虽然这不太美观,但在某些情况下至少不是关键问题。
在您想要启用逗号分隔符的控制器中,只需要调用一个方法即可。也许在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
我无法实现之前提出的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
def price=(val)
val = val.gsub(',', '')
super
end
config/initializers
目录下。 - panteo