问题描述
相关/已修复:Ruby on Rails: Validations on Form Object are not working
我有以下验证。
validates :age,numericality: { greater_than_or_equal_to: 0,only_integer: true,:allow_blank => true
}
如果输入的是数字,则不需要。我注意到,如果有人输入单词而不是数字,则提交并通过验证后,字段值将更改为0。我希望它为空白或输入的值。
更新:
仍然没有解决方案,但这是更多信息。
rspec测试 it "returns error when age is not a number" do
params[:age] = "string"
profile = Registration::Profile.new(user,params)
expect(profile.valid?).to eql false
expect(profile.errors[:age]).to include("is not a number")
end
Rspec测试失败:
Registration::Profile Validations when not a number returns error when age is not a number
Failure/Error: expect(profile.errors[:age]).to include("is not a number")
expected [] to include "is not a number"
2.6.5 :011 > p=Registration::Profile.new(User.first,{age:"string"})
2.6.5 :013 > p.profile.attributes_before_type_cast["age"]
=> "string"
2.6.5 :014 > p.age
=> 0
2.6.5 :015 > p.errors[:age]
=> []
2.6.5 :016 > p.valid?
=> true
module Registration
class Profile
include ActiveModel::Model
validates :age,:allow_blank => true
}
attr_reader :user
delegate :age,:age=,to: :profile
def validate!
raise ArgumentError,"user cant be nil" if @user.blank?
end
def persisted?
false
end
def user
@user ||= User.new
end
def teacher
@teacher ||= user.build_teacher
end
def profile
@profile ||= teacher.build_profile
end
def submit(params)
profile.attributes = params.slice(:age)
if valid?
profile.save!
true
else
false
end
end
def self.model_name
ActiveModel::Name.new(self,nil,"User")
end
def initialize(user=nil,attributes={})
validate!
@user = user
end
end
end
#Profile模型:
class Profile < ApplicationRecord
belongs_to :profileable,polymorphic: true
strip_commas_fields = %i[age]
strip_commas_fields.each do |field|
define_method("#{field}=".intern) do |value|
value = value.gsub(/[\,]/,"") if value.is_a?(String) # remove,self[field.intern] = value
end
end
end
有趣的是,如果将验证移至配置文件模型并检查p.profile.errors,则可以看到预期的结果,但在窗体对象上却看不到。我需要将验证保留在表单对象上。
解决方法
如果数据库中的基础列是数字类型,则Rails强制使用该值。我认为这是在[ActiveRecord :: Type :: Integer#cast_value] [1]中完成的
def cast_value(value)
value.to_i rescue nil
end
假设model
是ActiveRecord模型,其中age
是整数列:
irb(main):008:0> model.age = "something"
=> "something"
irb(main):009:0> model.age
=> 0
irb(main):010:0>
这是因为提交表单将始终提交键值对,其中键值是字符串。 无论您的数据库列是数字,布尔值,日期还是...
它与验证本身无关。
您可以像这样在类型转换之前访问值:
irb(main):012:0> model.attributes_before_type_cast["age"]
=> "something"
如果您的要求决定了另一种行为,则可以执行以下操作:
def age_as_string=(value)
@age_as_string = value
self.age = value
end
def age_as_string
@age_as_string
end
然后在您的表单(或其他任何表单)中使用age_as_string
。您也可以为此属性添加验证,例如:
validates :age_as_string,format: {with: /\d+/,message: "Only numbers"}
您还可以添加自定义类型:
class StrictIntegerType < ActiveRecord::Type::Integer
def cast(value)
return super(value) if value.kind_of?(Numeric)
return super(value) if value && value.match?(/\d+/)
end
end
并通过“ Attributes API”在ActiveRecord类中使用它:
attribute :age,:strict_integer
如果您要分配的值无效,这将保留age
属性nil
。
ActiveRecord :: Type.register(:strict_integer,StrictIntegerType) [1]:https://github.com/rails/rails/blob/fbe2433be6e052a1acac63c7faf287c52ed3c5ba/activemodel/lib/active_model/type/integer.rb#L34
,为什么不在前端添加验证?您可以使用<input type="number" />
代替<input type="text" />
,后者仅接受来自用户的号码。我看到您解释问题的方式,这是要在前端而不是后端解决的问题。
您可以在此处了解更多信息:Number Type Input
如果这不适用于您,请告诉我,我们很乐意为您提供帮助。