[ruby on rails]ActiveModel源码阅读(Validations)

打印 上一主题 下一主题

主题 893|帖子 893|积分 2679

Validations



  • 类方法

  • validate 自定义验证方法 (不需要属性,也不需要校验器;直接做校验)
  1. class Invoice < ApplicationRecord
  2. validate :active_customer, on: :create
  3. def active_customer
  4.    errors.add(:customer_id, "is not active") unless customer.active?
  5. end
  6. end
复制代码

  • validates 和 validates! (需要属性,也需要校验器;间接做校验)
  1. class Person < ApplicationRecord
  2.   validates :name, presence: true #校验器是PresenceValidator
  3. end
  4. #校验器PresenceValidator
  5. class PresenceValidator < ActiveModel::EachValidator
  6.   def validate_each(record, attribute, value)
  7.     record.errors.add attribute, "can't be blank" if value.blank?
  8.   end
  9. end
复制代码

  • validates_each (需要属性,不需要校验器;直接做校验)
  1. class Person < ApplicationRecord
  2.   validates_each :name, :surname do |record, attr, value|
  3.     record.errors.add(attr, 'must start with upper case') if value =~ /\A[[:lower:]]/
  4.   end
  5. end
复制代码

  • validates_with 验证交给其他类做验证 (不需要属性,需要校验器;作用于所有对象,间接做校验)
  1. class GoodnessValidator < ActiveModel::Validator
  2.   def validate(record)
  3.           # options = { :fields => [:first_name, :last_name] }
  4.           # options = { :abc => [:first_name, :last_name] }
  5.     if options[:fields].any?{|field| record.send(field) == "Evil" }
  6.       record.errors[:base] << "This person is evil"
  7.     end
  8.   end
  9. end
  10. class Person < ApplicationRecord
  11.         #fields是options哈希的一个key,也可以是别的例如 abc: [:first_name, :last_name]
  12.   validates_with GoodnessValidator, fields: [:first_name, :last_name]
  13. end
复制代码
履历:牢记 validate 发生在 save 之前,如果你喜欢用 before_save 之类的举行查验,记得加上 return false 或者改变习惯,用 validate :validate_method 雷同写法举行校验。
Validator



  • 自定义验证类(校验器)

  • 自定义的验证类继承自 ActiveModel::Validator,必须实现 validate 方法,其参数是要验证的记录,然后验证这个记录是否有效。自定义的验证类通过 validates_with 方法调用
  1. class MyValidator < ActiveModel::Validator
  2.   def validate(record)
  3.     unless record.name.starts_with? 'X'
  4.       record.errors[:name] << 'Need a name starting with X please!'
  5.     end
  6.   end
  7. end
  8. class Person
  9.   include ActiveModel::Validations
  10.   validates_with MyValidator
  11. end
复制代码

  • 自定义的验证类中验证单个属性,最简单的方法是继承 ActiveModel::EachValidator 类。此时,自定义的验证类必须实现 validate_each 方法。这个方法继承三个参数:记录、属性名和属性值。它们分别对应模型实例、要验证的属性及其值。
  1. class EmailValidator < ActiveModel::EachValidator
  2.   def validate_each(record, attribute, value)
  3.     unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
  4.       record.errors[attribute] << (options[:message] || "is not an email")
  5.     end
  6.   end
  7. end
  8. class Person < ApplicationRecord
  9.   # presence: true为内置的验证类,email: true为自定义的验证类,可以同时使用
  10.   # email: true 对应的验证类必须是EmailValidator, title:true 对应的验证类必须是TitlelValidator
  11.   validates :email, presence: true, email: true
  12. end
复制代码


  • 自定义验证方法
    自定义验证方法必须使用类方法 validate 注册,传入自定义验证方法名的符号情势
    这个类方法可以继承多个符号,自定义的验证方法会按照注册的顺序执行
    可以设置 n 选项,指定什么时间验证。n 的可选值为 :create 和 :update。
  1. class Invoice < ApplicationRecord
  2.   validate :expiration_date_cannot_be_in_the_past,
  3.     :discount_cannot_be_greater_than_total_value
  4.   def expiration_date_cannot_be_in_the_past
  5.     if expiration_date.present? && expiration_date < Date.today
  6.       errors.add(:expiration_date, "can't be in the past")
  7.     end
  8.   end
  9.   def discount_cannot_be_greater_than_total_value
  10.     if discount > total_value
  11.       errors.add(:discount, "can't be greater than total value")
  12.     end
  13.   end
  14. end
复制代码


  • valid? 校验是否通过,只有一个判断规则:当前 record 对象的 errors 值是否为空 errors.empty?
    以是自己写校验方法或者校验器的时间,请记得设置 errors 的值。
Concern实现

  1. # app/models/concerns/custom_validatable.rb
  2. module CustomValidatable
  3.   extend ActiveSupport::Concern
  4.   included do
  5.     validate :custom_validate
  6.   end
  7.   def validate_custom_fields(fields)
  8.     CustomValidation.order(:position).each do |validation|
  9.       regex = Regexp.new(validation.regex)
  10.       fields.each do |field|
  11.         value = send(field)
  12.         unless value.match?(regex)
  13.           errors.add(field, validation.error_message)
  14.         end
  15.       end
  16.     end
  17.   end
  18. end
复制代码
  1. # app/models/issue.rb
  2. include CustomValidatable
  3. def custom_validate
  4.   validate_custom_fields(['subject', 'description'])
  5. end
复制代码
validates_with实现

  1. class WxMsgSecValidator < ActiveModel::Validator
  2.   # scene: 1 scene_profile 资料, 2 scene_comment 评论, 3 scene_post 论坛, 4 scene_log 社交日志
  3.   def validate(record)
  4.     if options[:fields].select { |field| record.send("#{field}_changed?") }.present?
  5.       case record.class.name
  6.       when 'YouKe'
  7.         @you_ke = record
  8.       else
  9.         @you_ke = record.you_ke
  10.       end
  11.       scene_hash = { desc: 2, content: 2 }
  12.       @you_ke = YouKe.where(is_robot: false).first if @you_ke&.is_robot?
  13.       open_id = @you_ke&.klly? ? @you_ke&.open_id : @you_ke&.wmh_open_id
  14.       return record.errors[:base] << '账号openid不存在' if open_id.nil?
  15.       options[:fields].select { |field| record.send("#{field}_changed?") }.each do |field|
  16.         if record.send(field).present?
  17.           is_deny = @you_ke.klly? ? !Weixin.msg_check(scene_hash[field], open_id, record.send(field)) : !WmhWeixin.msg_check(scene_hash[field], open_id, record.send(field))
  18.           record.errors.add(:base, '输入涉嫌违规') if is_deny
  19.         end
  20.       end
  21.     end
  22.   end
  23. end
复制代码
  1. # app/models/issue.rb
  2. include ActiveModel::Validations
  3. validates_with WxMsgSecValidator, fields: %i[content]
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

知者何南

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表