问题
This is such a dumb question that I feel like I must be missing something simple. I have a form with a quantity field. People keep typing commas when they enter the quantity (for example, they type 12,000 to indicate twelve thousand) so I would like to strip out the commas before saving the quantity (12000 in the example) into an integer column in the database.
So far I have tried two methods. Overriding the setter method as suggested in this SO question
def quantity=(num)
num.gsub!(',', '') if num.is_a?(String)
self[:quantity] = num.to_i
end
This works in a sense. If I type 12,000 in the quantity field and submit the form, I get 12000 in the database. The problem is that it also bypasses all quantity validations. No longer can I validate the presence of a value for quantity for example. This is not good.
I have also tried to use a before validation callback (instead of overriding the setter):
before_validation :sanitize_quantity
def sanitize_quantity
# Doesn't wok because quantity.to_i already called by this point.
end
This doesn't work because by the time the quantity has reached the callback method, Rails has already called to_i on it. This means that 12,000 will be truncated to 12 by the time it reaches the callback. Once again, not good.
What else can I try aside from stripping the commas on the client-side?
As a final note, I am aware that this is essentially a dumb thing to do because certain countries use periods and commas in different ways. I still need to do it anyway.
回答1:
Use Type Coercions
If I understand your question correctly, you're trying to coerce strings into numbers. If so, you can just use an explicit cast like so:
validates :quantity, presence: true, numericality: true
def quantity=(num)
self[:quantity] = num.to_s.scan(/\b-?[\d.]+/).join.to_f
end
Tests and Examples
See see how this works, you can try the following at the console.
# String as input.
number = '12,956.05'
number.to_s.scan(/\b-?[\d.]+/).join.to_f
=> 12956.05
# Float as input.
number = 12956.05
number.to_s.scan(/\b-?[\d.]+/).join.to_f
=> 12956.05
# Using an ActiveRecord object.
q = Quantity.new quantity: '12,956.05'
=> #<Quantity id: nil, quantity: 12956.05, created_at: nil, updated_at: nil>
q.save
=> true
回答2:
I actually figured this out just as I was reading over my question.
The key is to use a virtual attribute.
class Job < ActiveRecord::Base
def flexible_quantity
quantity
end
def flexible_quantity=(quantity)
self.quantity = quantity.gsub(',', '') unless quantity.blank?
end
end
Then in the form, use the virtual attribute.
<%= f.text_field :flexible_quantity %>
Now validate the presence of the flexible_quantity value instead.
class Job < ActiveRecord::Base
validates :flexible_quantity, presence: true
# The cool this is that we can leave the numericality validation on the quantity field.
validates :quantity, presence: true, numericality: { only_integer: true }
end
回答3:
You'll get the quantity in params[:quanity]. You can just call a helper method (defined in your controller, for instance) to fix the String, and then pass that along to your model.
# YourController.rb
def create # or whatever
quantity = sanitize_quantity(params[:quantity])
YourModel.create!(:quantity => quantity)
end
private
def sanitize_quantity(num)
num.gsub!(',', '') if num.is_a?(String)
return num.to_i
end
So you sanitize it before it goes to your model, so it can still have validations done on it.
来源:https://stackoverflow.com/questions/11585062/allowing-commas-in-a-numerical-field