ActiveRecord 的 Validation 用于实现表单验证非常方便,但某些时候它却会带来一些烦恼,因为使用不当会导致你的程序内部保存数据的时候过不了验证。
最简单、常见的验证我们可能会写成这样
class User < ApplicationRecord
validates :name, :email, :password, :id_card, presence: true
validates :name, length: { in: 2..10 }
end
我们分别有:注册 和 个人设置 两个表单
由于为了能让用户尽快注册,没要求用户一定要填写 name
和 id_card
,而 email
和 password
要求必填
要求 name
和 id_card
必填,这个表单没有密码,所以密码是不要求必填的
由于上面的 Validation 声明,导致注册表单没有 name
和 id_card
,验证无法通过。
在 Rails 4 里面我们可能会用 on: :create
和 on: :update
来缓解,但某些更复杂,更多需要验证的场景里面,update 都是有好多情况的,比如有个流程:
我们需要用户填写完成 a1 - a10,这些字段都有不同的验证条件,而且都是必填项!
这些字段都在同一个 Model,Step 1 可能可以用 on: :create
来实现不同的验证,但到了 2、3、4 部,都是同一个 Model 的 update
动作。
验证无法直接在 Model 里面写了...
于是我们一般都会单独写针对不同步骤的验证函数...或者更土鳖 的在 Controller 里面直接验证...
def step1
@issue.errors.add('a1', '不能为空') if ...
@issue.errors.add('a1', '的长度要求 10 - 20 个字符') if ...
@issue.errors.add('a2', '不是一个有效的手机号') if ...
end
def step2
@issue.errors.add('a4', '身份证格式不正确') if ...
@issue.errors.add('a5', '至少要选择一项') if ...
end
Rails 5 给我们带了解决方案 ,validates
的 :on
参数可以自定义上下文名称了
http://guides.rubyonrails.org/active_record_validations.html#on
class User < ApplicationRecord
validates :email, :password, presence: true, on: :sign_up
validates :password, confirmation: true, on: :sign_up
validates :email, format: { with: /.../ }, on: :sign_up
validates :name, :id_card, presence: true, on: :setting
validates :name, length: { in: 2..10 }, on: :setting
end
class Issue < ApplicationRecord
validates :a1, :a2, :a3, presence: true, on: :step1
validates :a1, length: { in: 10..20 }, on: :step1
validates :a2, format: { with: /.../ }, on: :step1
validates :a4, :a5, :a6, presence: true, on: :step2
validates :a4, format: { with: /.../ }, on: :step2
validates :a7, :a8, :a9, presence: true, on: :step3
validates :a10, presence: true, on: :step4
end
def step1
@issue.save(context: :step1)
end
def step2
@issue.save(context: :step2)
end