表单对象上的Rails验证数量失败



相关/已修复:RubyonRails:表单对象上的验证不工作

我有以下验证。。

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, numericality: { greater_than_or_equal_to: 0, 
only_integer: true, 
: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

#配置文件模型:

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,我会看到预期的结果,但不会在我的表单对象上看到。我需要在我的表单对象上保留我的验证。

如果DB中的底层列是数字类型,那么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>

这是因为提交表单将始终提交键值对,其中键值是字符串。无论您的DB列是数字、布尔值、日期还是。。。

它与验证本身无关。

您可以访问类型转换之前的值,如下所示:

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

并通过";属性API":

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" />,后者只接受用户的号码。我看到你解释这个问题的方式,这是一个需要在前端而不是后端解决的问题。

你可以在这里阅读更多信息:数字类型输入

如果这对你不起作用,请告诉我,我很乐意帮助你。

最新更新