有时想跳过你的数据库模型上的某些验证。也许你有一个多步骤的向导,或者希望管理员在改变数据时有更多的自由。
你可能会想让某些表单跳过验证,但有一个更好的方法。
Rails允许你在保存或验证一条记录时传入一个context 。你可以把context 和on: 选项结合起来,只运行某些ActiveRecord验证。
使用方法
假设你想为一个招聘网站建立一个多步骤的工作流程。你可能允许某人创建一个工作列表并开始填写数据,但直到发布列表时才完全验证所有内容:
class Listing < ApplicationRecord
belongs_to :company
belongs_to :user
has_rich_text :requirements
validates :title, presence: true, length: { maximum: 50 }
validates :salary_range, presence: true, on: :publish
validates :application_instructions, presence: true, on: :publish
def publish!
self.published_at = Time.current
save(context: :publish)
end
end
在这种情况下,每当我们创建或编辑这条记录时,我们将始终需要一个title (最多50个字符)。但是当我们把:publish 作为验证背景时,我们将只验证salary_range 和application_instructions 。
你可以用控制器动作来实现这个工作流程,比如:
class ListingsController < ApplicationController
def create
@listing = Listing.new(listing_params)
if @listing.save
redirect_to @listing, notice: "Listing created"
else
render :new, status: :unprocessable_entity
end
end
def publish
@listing = Listing.find(params[:id])
if @listing.publish!
redirect_to @listing, notice: "Listing published"
else
render :edit, status: :unprocessable_entity
end
end
end
你也可以根据哪个用户进行修改而添加不同的验证。也许你想让管理员给你的朋友提供特殊的、简短的用户名。
在这里,我们可以设置一条规则,要求在:create 上下文(Rails在创建记录时默认会包含)中的六个字符用户名。然后我们在:admin 上下文中添加一条规则,只要求三个字符:
class Account < ApplicationRecord
validates :username, length: { minimum: 6 }, on: :create
validates :username, length: { minimum: 3 }, on: :admin
end
Account.new(username: "swanson").valid? # => true
Account.new(username: "swanson").valid?(:admin) # => true
Account.new(username: "mds").valid? # => false
Account.new(username: "mds").valid?(:admin) # => true
Account.new(username: "a").valid? # => false
Account.new(username: "a").valid?(:admin) # => false
使用Rails验证上下文的一个缺点是,你可能无法使用数据库级别的验证。能够坚持部分有效的记录或有条件的规则是一个强大的功能,但它不是没有代价的。
请仔细考虑将验证从数据库约束层转移到你的应用程序。