ruby-on-rails - 使用 Rails 仅触发特定操作的某些验证并设计
问题描述
我有和管理员模型与设计:
class Administrator < ApplicationRecord
validate :max_num_admins, on: :create
devise :invitable, :database_authenticatable,
:recoverable, :rememberable, :validatable
def max_num_admins
self.errors.add(:base,
I18n.t(
'activerecord.errors.models.administrator.max_reached'
)
) if Administrator.count > 3
end
end
“帐户所有者”可以通过管理仪表板添加管理员,这具有邀请管理员的效果(设计邀请方法邀请!):
def create
resource = resource_class.new(resource_params)
authorize_resource(resource)
if resource.valid?
Administrator.invite!(resource_params)
redirect_to(
[namespace, resource],
notice: translate_with_resource('create.success')
)
else
render :new, locals: {
page: Administrate::Page::Form.new(dashboard, resource)
}
end
end
如您所见,我想检查我的记录是否有效,特别是如果该帐户的管理员记录不超过 3 条。问题是当我打电话valid?
给resource
(管理员)时,我总是得到错误,因为在这个阶段,邀请管理员的唯一必要参数是有效的电子邮件,但会触发设计验证,因此我会因为未设置密码而收到错误消息。
什么是保持电子邮件和 max_num_admins 验证触发此操作而不是密码的一种干净方法?
解决方案
验证链是一个回调链,验证链的结果errors
毕竟是(返回true
if errors.empty?
),没有办法过滤(或fail early
),所以当你的模型设置设计时,所有设计的验证都添加到验证链中,没有过滤掉它们但删除设计的方法。所以我想出了一个想法,你应该定义一个special validation
范围,该范围将附加在验证链的末尾并强制执行errors.clear
(这样它就会忽略以前的设计验证),看看下面的代码:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:confirmable
validate :max_num_admins, on: [:create, :special] # should be the last validation below devise validations
def max_num_admins
if self.validation_context == :special
errors.clear # that mean previous validations (not only devise) be ignored
end
self.errors.add(:base,
I18n.t(
'activerecord.errors.models.administrator.max_reached'
)
) if Administrator.count > 3
end
end
然后
def create
# ...
if resource.valid?(:special) # validate at special scope
end
end
if resource.valid? # this will work as normal
更新
无需触及任何现有的验证,您可以在验证链的末尾添加一个验证special
,slice!
用于检查您想要检查的那些错误:
class User
# ...
validate :max_num_admins, on: [:create, :special]
validate :special, on: [:special]
def max_num_admins
self.errors.add(:max_num_admins,
I18n.t(
'activerecord.errors.models.administrator.max_reached'
)
) if Administrator.count > 3
end
private
def special
# removes all errors except the given keys.
# so if you want to check email and max_num_admins
errors.slice!(
:email,
:max_num_admins
)
end
end
然后
if resource.valid?(:special) # validate email and max_num_admins
if resource.valid? # check as normal password, email, ..., max_num_admins
注意弃用警告:ActiveModel::Errors#slice!已弃用并将在 Rails 6.2 中删除。
推荐阅读
- python - 为什么我不能在 Google Colab 中使用升级的 plotly?
- java - 验证sql spark java的列
- c - 关于 C 中使用 minGW-W64 的 stat 函数的新手问题
- cypress - 如何根据 `before()` 中收到的数据动态生成赛普拉斯测试?
- laravel - 如何在 laravel 中使用队列发送邮件和通知时更改邮件设置
- azure-active-directory - 乏味:Azure AD with accessToken Connection lost - 读取 ECONNRESET
- c++ - 来自多个线程的 stable_clock::now 的值是否与内存顺序一致?
- python - 名称未定义,即使使用全局变量?
- python - 我需要在此 python 代码中进行哪些更改才能将 DNA 序列转换为蛋白质?
- rust - move 发生是因为 value 的类型为 `RefCell<...>`,它没有实现 `Copy` 特征