Ruby on Rails:表单对象验证不起作用 [英] Ruby on Rails: Validations on Form Object are not working

查看:63
本文介绍了Ruby on Rails:表单对象验证不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

表单对象验证不起作用,我的代码有什么问题?

请阅读发布的两个案例.第一种情况有验证工作,第二种情况没有.

案例 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆