Rails方式:在模型中设置之前格式化值?

Lan*_*ard 6 activerecord ruby-on-rails

我有用户输入的表单字段:

  • 百分比: 50.5%
  • 钱: $144.99
  • 日期: Wednesday, Jan 12th, 2010

...

percentmoney类型的属性被保存为decimal与ActiveRecord的领域,以及日期datetimedate领域.

在javascript中很容易在格式之间进行转换,理论上你可以将它们转换为activerecord可接受的格式onsubmit,但这不是一个不错的解决方案.

我想做一些事情覆盖ActiveRecord中的访问器,所以当它们被设置时,它将它们从任何字符串转换为适当的格式,但这也不是最好的.

我不想要的是必须通过一个单独的处理器对象运行它们,这在控制器中需要这样的东西:

def create
  # params == {:product => {:price => "$144.99", :date => "Wednesday, Jan 12, 2011", :percent => "12.9%"}}
  formatted_params = Product.format_params(params[:product])
  # format_params == {:product => {:price => 144.99, :date => Wed, 12 Jan 2011, :percent => 12.90}}
  @product = Product.new(format_params)
  @product.save
  # ...
end
Run Code Online (Sandbox Code Playgroud)

我希望它完全透明.ActiveRecord中的钩子在哪里,所以我可以用Rails方式做到这一点?

更新

我现在只是这样做:https: //gist.github.com/727494

class Product < ActiveRecord::Base
  format :price, :except => /\$/
end

product = Product.new(:price => "$199.99")
product.price #=> #<BigDecimal:10b001ef8,'0.19999E3',18(18)>
Run Code Online (Sandbox Code Playgroud)

iai*_*ain 31

你可以覆盖setter或getter.

覆盖二传手:

class Product < ActiveRecord::Base
  def price=(price)
    self[:price] = price.to_s.gsub(/[^0-9\.]/, '')
  end
end
Run Code Online (Sandbox Code Playgroud)

覆盖吸气剂:

class Product < ActiveRecord::Base
  def price
    self[:price].to_s.gsub(/[^0-9\.]/, ''))
  end
end
Run Code Online (Sandbox Code Playgroud)

不同之处在于后一种方法仍然存储用户输入的任何内容,但检索格式化,而第一种方法存储格式化版本.

当你打电话Product.new(...)update_attributes等时会使用这些方法......


Doo*_*oon 12

您可以使用前验证挂钩来规范化您的参数,例如 before_validation

class Product < ActiveRecord::Base

     before_validation :format_params


      .....


     def format_params
        self.price = price.gsub(/[^0-9\.]/, "")
        ....
     end
Run Code Online (Sandbox Code Playgroud)

  • 如果你在你的应用程序中再次编辑这个值,你需要双向进行这个过程,例如你在before_validation中执行的任何过程,你需要通过覆盖getter来反转,请参阅iain对这个问题的回答以获得一个例子 (2认同)
  • 假设价格是数据库中的一个数字,如上所述,它将在类型转换后发生。这意味着“1ab23cd45”的用户输入将在看到 format_params 之前将 to_f 处理为 1。经过漫长的一天,我现在知道应该是 self.price = price_before_type_cast.gsub([/[^0-9\.]/,'') 使用自动生成的 *_before_type_cast 访问器是一个关键的秘密。 (2认同)