Rails Rails 验证 on: :context

torvaldsdb · 2018年07月29日 · 793 次阅读

数据验证 给出一个实例model -- User

class User < ActiveRecord::Base
  validate :first_name, presence: true  # case 1
  validate :last_name, presence: true, on: :create # case 2
  validate :nickname, presence: true, on: :update # case 3
  validates :balance, numericality: true, on: :admin # case 4 一种定制的 context
end

构造 User 数据:

FactoryGirl.define do
  factory :user do
    first_name { Faker::Name.first_name }
    last_name { Faker::Name.first_name }
    nickname { Faker::Name.name }
    balance { Faker::Number.number(6) }

    factory :user_without_first_name do
      first_name nil
    end

    factory :user_without_last_name do
      last_name nil
    end

    factory :user_without_nickname do
      nickname nil
    end

    factory :user_without_balance do
      balance nil
    end

    factory :user_with_negative_balance do
      balance { Faker::Number.negative(10, 20) }
    end
  end
end

详细解释一下示例中的四种校验 假设: user 是 User 的一个实例.

  1. first_name 的验证, 没有定义context(没有on: **的存在), 这种验证在 user 每次save的时候都会被触发.
  2. last_name 的验证, 定义了context(on: :create), 该验证只有 user 是新建数据, 满足#new_record?true, save的时候触发.
  3. nickname 的验证, 同样定义了context(on: :update), 该验证理论上应该在更新数据(数据user#persisted?true)的时候才会触发, 测试中 on: :update == on: [:create, :update], 无论是新建数据还是更新数据都会触发.

    # 验证on: :create
    # 新建一个没有 last_name 的 user
    user_without_last_name = FactoryGirl.build(:user_without_last_name)
    user_without_last_name.valid? # => false
    # 更新一个没有 last_name 的 user
    user_without_last_name.save(validate: false)
    user_without_last_name.valid? # => true
    
    # 验证on: :update
    # 新建一个没有 nickname 的 user
    user_without_nickname = FactoryGirl.build(:user_without_nickname)
    user_without_nickname.valid? # => false
    # 更新一个没有 nickname 的 user
    user_without_nickname.save(validate: false)
    user_without_nickname.valid? # => false
    
  4. balance 的验证, 自定义了context(on: :admin), 这种方式需要特殊的触发方式: context 源码

    # Runs all the validations within the specified context. Returns +true+ if
    # no errors are found, +false+ otherwise.
    #
    # If the argument is +false+ (default is +nil+), the context is set to <tt>:create</tt> if
    # <tt>new_record?</tt> is +true+, and to <tt>:update</tt> if it is not.
    #
    # Validations with no <tt>:on</tt> option will run no matter the context. Validations with
    # some <tt>:on</tt> option will only run in the specified context.
    def valid?(context = nil)
      context ||= (new_record? ? :create : :update)
      output = super(context)
      errors.empty? && output
    end
    

    使用:

    user = FactoryGirl.build(:user)
    # valid?
    user.valid?(:admin)
    
    # save
    user.save(context: :admin)
    

注意点:

user = FactoryGirl.build(:user)
  1. 当你运行user.valid?(:admin)的时候, 只会验证first_name, 与balance, 并不会验证last_name nickname
  2. on: :admin 做为允许的条件, 可以在保存的时候 #save(context: :admin), 如果默认保存的手做其他的限制, 可以:

    validates :balance, numericality: true, on: :admin
    
    validates :balance, numericality: { greater_than_or_equal_to: 0 }, 
                        on: [:create, :update]
    

    这样 user.save 就会限制 balance 大于 0

  3. context 在 autosave 中的使用: 添加 Article

    class Article < ActiveRecord::Base
    belongs_to :user, autosave: true
    end
    

    构造 Article 数据:

    FactoryGirl.define do
    factory :article do
    user
    end
    end
    

    使用:

    user = FactoryGirl.create(:user)
    article = FactoryGirl.create(:article, user: user)
    
    article.user.assign_attributes(balance: -1)
    
    # valid?
    article.valid? # => true
    # valid?(:admin)
    article.valid?(:admin) # => false
    
    # save
    article.save # => true
    # save(:admin)
    article.save(:admin) # => false
    
  4. context 在测试中写法

    require 'rails_helper'
    
    RSpec.describe User, type: :model do
      it { expect(build(:user_without_balance)).to be_valid(:admin) }
    end
    
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册