Ruby on Rails:表单对象验证不起作用 [英] Ruby on Rails: Validations on Form Object are not working
问题描述
表单对象验证不起作用,我的代码有什么问题?
请阅读发布的两个案例.第一种情况有验证工作,第二种情况没有.
案例 1
#Profile 模型:
类简介<申请记录归属地:可配置文件,多态:真validates_presence_of :age验证:年龄,数字:{大于或等于:0,only_integer: 真,:allow_blank =>真的}结尾
来自控制台的验证测试:
p= Profile.new =>#<个人资料ID:无,年龄:无>p.age = 字符串"=>字符串"p.save =>错误的p.errors.full_messages=>[Profileable 必须存在",年龄不是数字"]Profile.create(age:string").errors.full_messages=>[Profileable 必须存在",年龄不是数字"]
直接在模型上进行验证
案例 2
#Form 对象注册:配置文件:
模块注册班级简介包括 ActiveModel::Modelvalidates_presence_of :age验证:年龄,数字:{大于或等于:0,only_integer: 真,:allow_blank =>真的}attr_reader : 用户委托 :age , :age=, to: :profiledef坚持?错误的结尾定义用户@user ||= User.new结尾老师@teacher ||= user.build_teacher结尾定义配置文件@profile ||= Teacher.build_profile结尾定义保存如果有效?个人资料.保存!真的别的错误的结尾结尾def提交(参数)profile.attributes = params.slice(:age)如果有效?个人资料.保存!结尾自己结尾def self.model_nameActiveModel::Name.new(self, nil, "User")结尾def 初始化(用户=nil,属性={})@用户 = 用户结尾结尾结尾
#Profile 模型:
类简介<申请记录归属地:可配置文件,多态:真结尾
控制台对表单对象的验证测试不起作用
a=Registration::Profile.new(User.first)a.age = 字符串"a.保存=>真的a.errors.full_messages=>[]
它返回 0
因为它将 age
访问器委托给 profile
模型.当您设置它时,它会将其传递给底层配置文件,该配置文件会跟踪您设置的值(在 profile.attributes_before_type_cast
中),但是当您调用 age
获取它(委托人所做的),它返回类型转换值(在 profile.attributes
中)
p = Profile.new age: omg";p.attributes_before_type_cast # =>{id"=>nil,user_id"=>nil,age"=>omg"}p.attributes # =>{id"=>nil,user_id"=>nil,age"=>0}页数 # =>0
我修改了您的示例以将属性存储在 Registration::Profile
实例上,并且仅在通过活动模型的验证后才将它们复制过来.有多种方法可以做到这一点,但我使用了 ActiveModel::Attributes
来做到这一点,所以它的行为就像 ActiveRecord
,因为这可能是最熟悉和兼容的.>
现在,不是将 age
委托给配置文件,而是使用 attribute :age
声明它.如果您不想要,您不必使用这些,但您不想将其存储在底层配置文件对象上(例如,如果您愿意,您可以使用 attr_accessor
,但随后您在保存基础配置文件之前,还必须手动构建您传递的哈希值).
这是我的版本:
# 设置一个数据库来测试它需要'active_record'ActiveRecord::Base.establish_connection 适配器:'sqlite3',数据库:':memory:'ActiveRecord::Schema.define 做self.verbose = falsecreate_table :用户做 |t|t.string :name结尾create_table :profiles 做 |t|t.integer :user_idt.整数:年龄结尾结尾# 底层模型用户 = Class.new(ActiveRecord::Base) { has_one :profile }配置文件 = Class.new(ActiveRecord::Base) {belongs_to :user }# 包装模型模块注册班级简介attr_reader :user, :profile包括 ActiveModel::Attributes属性:年龄包括 ActiveModel::Modelvalidates_presence_of :age验证:年龄,数字:{greater_than_or_equal_to:0,only_integer:true,allow_blank:true}定义初始化(用户)@用户 = 用户@profile = user.profile ||user.build_profile# 设置@attributes极好的()# 从配置文件的当前属性开始self.attributes = profile.attributes.slice(*@attributes.keys)结尾定义保存除非有效,否则返回 false?profile.attributes = attributes # 将我们的属性复制到底层模型个人资料.保存!# 我们希望它能够保存,所以如果没有就爆炸结尾结尾结尾u = User.create!p = 注册::Profile.new(u)# 无效示例p.age = 字符串"p.save # =>错误的p.errors.full_messages # =>[年龄不是数字"]页数 # =>字符串"p.profile.age # =>零p.profile.persisted?# =>错误的# 有效示例页码=123"p.save # =>真的p.errors.full_messages # =>[]页数 # =>123"p.profile.age # =>123p.profile.persisted?# =>真的# 使用现有配置文件初始化注册::Profile.new(u).age # =>123
Validations on Form Object does not work, What is wrong with my code?
Please read the two cases posted. The first case has validation working, the second case does not.
Case 1
#Profile Model:
class Profile < ApplicationRecord
belongs_to :profileable, polymorphic: true
validates_presence_of :age
validates :age, numericality: { greater_than_or_equal_to: 0,
only_integer: true,
:allow_blank => true
}
end
Validation Test from Console:
p= Profile.new => #<Profile id: nil, age: nil>
p.age = "string" => "string"
p.save => False
p.errors.full_messages
=> ["Profileable must exist", "Age is not a number"]
Profile.create(age:"string").errors.full_messages
=> ["Profileable must exist", "Age is not a number"]
Validation directly on the model works
Case 2
#Form Object Registration:Profile:
module Registration
class Profile
include ActiveModel::Model
validates_presence_of :age
validates :age, numericality: { greater_than_or_equal_to: 0,
only_integer: true,
:allow_blank => true
}
attr_reader :user
delegate :age , :age=, to: :profile
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 save
if valid?
profile.save!
true
else
false
end
end
def submit(params)
profile.attributes = params.slice(:age)
if valid?
profile.save!
end
self
end
def self.model_name
ActiveModel::Name.new(self, nil, "User")
end
def initialize(user=nil, attributes={})
@user = user
end
end
end
#Profile Model:
class Profile < ApplicationRecord
belongs_to :profileable, polymorphic: true
end
Validation Test from Console on form object does not work
a=Registration::Profile.new(User.first)
a.age = "string"
a.save => true
a.errors.full_messages
=> []
It is returning 0
because it delegates the age
accessor to the profile
model. When you set it, it passes that through to the underlying profile, which keeps track of the value that you set (in profile.attributes_before_type_cast
), but when you call the age
getter on it (which the delegator does), it returns the typecast value instead (in profile.attributes
)
p = Profile.new age: "omg"
p.attributes_before_type_cast # => {"id"=>nil, "user_id"=>nil, "age"=>"omg"}
p.attributes # => {"id"=>nil, "user_id"=>nil, "age"=>0}
p.age # => 0
I modified your example to store the attributes on the Registration::Profile
instance, and only copy them over after passing the active model's validations. There's multiple ways to do this, but I used ActiveModel::Attributes
to do that, so that it would behave like ActiveRecord
, since that's probably most familiar and compatible.
Now, instead of delegating the age
to the profile, you declare it with attribute :age
. You don't have to use these if you don't want, but you don't want to store it on the underlying profile object (eg you could use an attr_accessor
if you wanted, but then you'd also have to manually build the hash you pass before saving the underlying profile).
Here's my version:
# Setup a database to test it with
require 'active_record'
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
ActiveRecord::Schema.define do
self.verbose = false
create_table :users do |t|
t.string :name
end
create_table :profiles do |t|
t.integer :user_id
t.integer :age
end
end
# The underlying models
User = Class.new(ActiveRecord::Base) { has_one :profile }
Profile = Class.new(ActiveRecord::Base) { belongs_to :user }
# The wrapping model
module Registration
class Profile
attr_reader :user, :profile
include ActiveModel::Attributes
attribute :age
include ActiveModel::Model
validates_presence_of :age
validates :age, numericality: { greater_than_or_equal_to: 0, only_integer: true, allow_blank: true }
def initialize(user)
@user = user
@profile = user.profile || user.build_profile
# to set @attributes
super()
# start with the profile's current attributes
self.attributes = profile.attributes.slice(*@attributes.keys)
end
def save
return false unless valid?
profile.attributes = attributes # copy our attributes to the underlying model
profile.save! # we expect it to save, so explode if not
end
end
end
u = User.create!
p = Registration::Profile.new(u)
# Invalid example
p.age = "string"
p.save # => false
p.errors.full_messages # => ["Age is not a number"]
p.age # => "string"
p.profile.age # => nil
p.profile.persisted? # => false
# Valid example
p.age = "123"
p.save # => true
p.errors.full_messages # => []
p.age # => "123"
p.profile.age # => 123
p.profile.persisted? # => true
# Initialize with an existing profile
Registration::Profile.new(u).age # => 123
这篇关于Ruby on Rails:表单对象验证不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!