Rails Rails 验证 on: :context

torvaldsdb · July 29, 2018 · 1442 hits

数据验证 给出一个实例 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
    
No Reply at the moment.
You need to Sign in before reply, if you don't have an account, please Sign up first.