validate的条件

#会进行validate验证
create
create!
save
save!
update
update!

#会跳过validate验证
save(validate: false)

验证是否有效的方法

valid?
invalid?

#实例
class Article < ApplicationRecord
  validates :title, presence: { message: "can't be blank" }
end

@article = Article.new
@article.valid? #=> false
@article.invalid? #=> true

@article = Article.create
@article.valid? #=> false
@article.invalid? #=> true

validates帮助方法

acceptance
validates_associated
confirmation
exclusion
format
inclusion
length
numericality
presence
absence
uniqueness
validates_with
validates_each
#使用场景,接受网站的条款
#acceptance 
rails g scaffold tag name
#model
attr_accessor :terms_of_service #建立虚拟属性
validates :terms_of_service, acceptance: true
#controller
params.require(:tag).permit(:name, :terms_of_service)
#view
<%= form.check_box :convention %>  #默认从check_box传递进来的参数是1或0,model中默认接受参数是['1', true]
#model默认接受的参数是['1', true],但是view中如果输入true,被转化成字符串‘true’,因此需要转换
#改变veiw,使之传递的参数值是'accepted'
#model
validates :terms_of_service, acceptance: {accept: 'accepted'}
#view
<%= form.select :convention, ['accepted', 'no'] %>
#使用场景,在关联对象上
#validates_associated 
#示例 tag has_many articles
class Tag < ApplicationRecord
  has_many :books #必须建立关联条件
  validates_associated :books #不能两边都设置validates_associated, 否则陷入死循环
end
#使用场景:使用在重要的数据两次确认上,比如说密码
#confirmation
rails g migration add_password_to_tag password:string
#model
class Tag < ApplicationRecord
  attr_accessor :password_confirmation
  validates :password_confirmation, confirmation: true
  validates :password, presence: true
end
#controller
params.require(:tag).permit(:name, :password, :password_confirmation)
#view
<%= form.password_field :password %>
<%= form.password_field :password_confirmation %>
#注意,对于password字段,需要在日志中进行过滤,使用的方法为config/filter_parameter_logging.rb
Rails.application.config.filter_parameters += [:password]
#使用场景:检查保留字段
#exclusion
class Account < ApplicationRecord
  #in和within的作用一致
  validates :subdomain, exclusion: { in: %w(www us ca jp), message: "%{value} is reserved." }
end
#使用场景:字符匹配
#format
class Product < ApplicationRecord
  #with是匹配字段,without是不能匹配字段
  validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,
    message: "only allows letters" }
end
#使用场景:指定字符集
#inclusion
class Coffee < ApplicationRecord
  #in和within的作用一致
  validates :size, inclusion: { in: %w(small medium large),
    message: "%{value} is not a valid size" }
end
#使用场景:判断字符长度
#length
class Person < ApplicationRecord
  #可选,minimum,maximum,in,is
  validates :name, length: { minimum: 2 }
  validates :bio, length: { maximum: 500 }
  validates :password, length: { in: 6..20 }
  validates :registration_number, length: { is: 6 }
end
#使用场景:判断为数字
#numericality
class Player < ApplicationRecord
  #validates :points, format: { with: /\A[+-]?\d+\z/, message: "only number" } #和下面功能一致,注意匹配开头结尾是\A\z
  validates :points, numericality: true
  #only_integer是可选项,其他可选项查看guides文档
  validates :games_played, numericality: { only_integer: true }
end
#使用场景:属性不为空
#presence
#调用presence,属性上调用blank?方法,若为nil或者“”则为true
#“”.blank? #=>true; " ".blank? #=>true; nil.blank? #=>true
#false.blank? #=>true; 所以需要排除这个字段,排除方法见guides
validates :name, presence: true  
#验证关联属性是否存在
class LineItem < ApplicationRecord
  belongs_to :order
  validates :order, presence: true
end
class Order < ApplicationRecord
  has_many :line_items, inverse_of: :order #验证关联对象是否存在,一定要指定inverse_of
end
#使用场景:属性为空
#absence
#调用presence,属性上调用present?方法,若为nil或者“”则为false
#false.present? #=>false; 所以需要排除这个字段,排除方法见guides
validates :name, absence: true
#验证关联属性是否存在
class LineItem < ApplicationRecord
  belongs_to :order
  validates :order, absence: true
end
class Order < ApplicationRecord
  has_many :line_items, inverse_of: :order ##验证关联对象是否存在,一定要指定inverse_of
end
#使用场景:属性值为唯一性
#uniqueness
validates :name, uniqueness: true
validates :name, uniqueness: { case_sensitive: false } #指定忽略大小写选项
#一般为了确保唯一性,在数据库层面要做唯一性索引
class AddNameIndexToTag < ActiveRecord::Migration[5.1]
  def change
    add_index :tags, :name, unique: true
  end
end

#为唯一性限定范围,指定在一个年度内数唯一
class Tag < ApplicationRecord
  validates :name, uniqueness: { scope: :year,
    message: "should happen once per year" }
end
#限定范围的索引唯一需要创建多键索引
class AddMultiIndexToTag < ActiveRecord::Migration[5.1]
  def change
    add_index :tags, [:name, :year], unique: true
  end
end
#场景:把验证抽象出来作为类,属于自定义验证类内容
#validates_with
#config/tag_validates.rb
class GoodnessValidator < ActiveModel::Validator
  def validate(record)
    if record.name == "hello"
      record.errors[:name] << "This person is evil"
    end
  end
end
#tag.rb
class Tag < ApplicationRecord
  validates_with GoodnessValidator
end
#定制的用法已经足够应付常见问题,详细用法见guides代码
#场景:针对每个属性做相同的验证,属于自定义验证类内容
#validates_each
class Person < ApplicationRecord
  validates_each :name, :surname do |record, attr, value|
    record.errors.add(attr, 'must start with upper case') if value =~ /\A[[:lower:]]/
  end
end

validates可选项

:allow_nil
:allow_blank
:message
:on

#allow_nil: true
nil?是Ruby Object方法
nil.nil? #true, 除了nil, 其他任何值nil?都为false
当待验证值为nil时,则跳过验证
validate

#allow_blank: true
blank?是Rails方法
nil.blank? #=>true, 在rails环境下
""blank? #=true,在rails环境下
当验证值为空或者为nil时候,则跳过验证

#message
message有两种用法,第一种是接受字符串,第二种是接受block对象
#第一种接受字符串对象
uniqueness: { message: "this is the message" } #常规用法
#通过"%{}"获得对应的值,其中value表示从view中传递过来的值,attribute表示需要验证的字段属性,model表表示字段所在model
uniqueness: { message: "%{value},%{attribute},%{model}"}
#第二种接受proc对象
uniqueness: {
  # object = 要验证的 tag 对象
  # data = { model: "Tag", attribute: "Name", value: <tag> }
  message: ->(object, data) do
    "Hey #{object.name}!, #{data[:value]} is taken already! Try again #{Time.zone.tomorrow}"
  end
}

#on
validates :name, uniqueness: { message: "should be unique", on: :create} #实例
在本篇章的最初,介绍在六种情况下会触发validates方法,分别是save,save!,create,create!,update,update!
默认情况下,没有指定on,上面提到的六种情况都会触发,若进行了指定,则
on: :create  #save,save!,create,create!才触发validates
on: :update  #update, !update!才触发

#on指定特定的上下文,缺乏示例

严格验证(基本用不到,只做示例)

#示例
class Person < ApplicationRecord
  validates :name, presence: { strict: true }
end
Person.new.valid?  # => 严格验证,抛出异常:ActiveModel::StrictValidationFailed: Name can't be blank

#验证指定异常类
class Person < ApplicationRecord
  #指定异常类
  validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
end
Person.new.valid?  # => TokenGenerationException: Token can't be blank

validates条件

if
unless

#就是做条件判断,只有在条件判断成立的时候才进行validates,接受判断的一般包括符号(指定方法)和proc对象
#使用符号(指定方法)
validates :name, presence: true, unless :accepted_blank?
private
  def accepted_blank?
    self.name == "" || self.name == nil
  end
#使用proc对象
validates :name, presence: true, unless: Proc.new { |tag| tag.name.blank? }

#简便用法,如上
validates :name, presence: true, allow_blank: true

自定义验证类和验证方法

#实际操作中比较少用到,详细说明见guides

处理errors

#示例代码
rails g scaffold tag name
class Tag < ApplicationRecord
  validates :name, presence: true
  validates :name, numericality: true
end
#rails c
@tag = Tag.new
@tag.valid? #=>false

@tag.errors #=>ActiveModel::Errors对象
@tag.errors.details #=>{:name=>[{:error=>:blank}, {:error=>:not_a_number, :value=>nil}]}
@tag.errors.messages #=>{:name=>["can't be blank", "is not a number"]}
@tag.errors.messages.values.flatten #=>["can't be blank", "is not a number"]
@tag.errors.full_messages #=>["Name can't be blank", "Name is not a number"]
@tag.errors.add(:name, "this is the demo") #=>["can't be blank", "is not a number", "this is the demo"]
@tag.errors[:name] #=>["can't be blank", "is not a number"]
@tag.errors.messages[:name] #=>["can't be blank", "is not a number"]
@tag.errors[:base] #=> "xx"
@tag.errors.messages #=> {:name=>["can't be blank", "is not a number", "this is the demo"], :base=>["xx"]}
@tag.errors.size #=>4
@tag.errors.count #=>4
@tag.errors.clear #=>{}

results matching ""

    No results matching ""