ActiveRecord 根据数据库列类型自动对输入进行类型转换。当 Ruby 将字符串转换为整数时,它会删除第一个非数字字符之后的所有内容,123-456-7890 将变为 123。这是在模型中的字段可用之前完成的,因此到目前为止给出的解决方案将不起作用。您需要覆盖默认的写访问器!
ActiveRecord::Base docs 提到了两种覆盖模型中默认写访问器(field_name)= 的方法,因此您可以在需要进行类型转换之前处理删除非数字的输入。至少有三种变体:
(1) 覆盖访问器,使用write_attribute将结果存入数据库:
def phone=(value)
value.gsub!(/\D/, '') if value.is_a?(String)
write_attribute(:phone, value.to_i)
end
(2) 或者使用哈希表示法:
def phone=(num)
num.gsub!(/\D/, '') if num.is_a?(String)
self[:phone] = num.to_i
end
(3) 或者(在最新版本的 ActiveRecord 中)只需调用 super 就好像它是一个普通(非动态)方法(文档中未显示但有效):
def phone=(num)
num.gsub!(/\D/, '') if num.is_a?(String)
super(num)
end
这在许多情况下都很有用,例如带逗号的数字,并且非常适用于提供前一个字段值的格式化版本的表单,例如在编辑表单中或在新/创建表单中出现错误之后:
<%= f.text_field :phone, number_to_phone(@model_data.phone) %>
这样您可以显示用户格式化的数据(即作为字符串),但仍将整数存储在数据库中。
关于 MySQL 的最后一个提示:您需要使用 BigInt 来存储电话号码,否则您会在数据库中看到很多 (214) 748-3647 电话号码,因为 2,147,483,647 是普通 MySQL 整数的最大值- int(11) - 在迁移中获得形式 :integer。 MySQL 中的BigInt 是通过将:limit 设置为8 获得的,如下迁移行:
change_column :model_name, :phone, :integer, :limit => 8
这将在您的表格中为您提供一个bigint(20),它可以处理高达9,223,372,036,854,775,807 的号码,这对于任何电话号码都应该足够了。