Rails 基于另一个模型的属性更新验证

Rails Validation On Attribute Update Based on Another Model

我有三个相关模型,一个帐户有一个程序和一个配置文件。创建账号时,profile.email可以为nil,只要program.upgraded == 'f'。但是在将 program.upgraded 设置为 't'.

之前,我该如何以及应该在哪里验证 profile.email 是否已设置,即不是零

所以我尝试创建自定义验证,但我可能没有正确实施。我有一项服务调用

program.update!(
      upgraded: 't',
    )

在异常救援的事务块内。这是我的验证:

class Account::Program < ApplicationRecord
  validate :upgraded, :upgraded_email_present
  ...
  private
  def upgraded_email_present
    return unless ppc_p == 't' && account.profile.auto_email.blank?
    errors[:upgraded] << "profile auto_email must be set"
  end
  ...
end

更新中!语句似乎失败了,它触发了救援,因为事务块中有正确的错误。然而,当我检查模型实例时,它仍然被升级了!完全搞不懂这里出了什么问题。

编辑

终于明白是怎么回事了。我在我的规范的上下文中测试验证并且必须对实例进行重新加载。此解决方案有效。

您可以使用自定义验证器 (Rails docs) 进行更复杂的验证,例如您所追求的。 ActiveModel::EachValidator 实现 .validate_each,它接受以下参数:

  1. 正在验证的记录
  2. 正在验证的属性
  3. 该属性的当前值
class UpgradeEmailPresentValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    # All good if :upgraded is false or if the profile email is present
    return unless value? && record.account.profile.email.blank?

    record.errors[attribute] << (options[:message] || "profile email must be set")
  end
end
 
class Program < ApplicationRecord
  validates :upgraded, presence: true, upgrade_email_present: true
end

你没有说 Program 是否通过 has_one :throughProfile 关联,但如果是,你可以将 record.account.profile 缩短为 record.profile.

编辑

我在原文中忘记提到您也可以在模型上使用自定义验证方法。

class Program < ApplicationRecord
  validate :upgraded, :upgrade_email_present

  def upgrade_email_present
   return unless upgraded? && account.profile.email.blank?

   errors[:upgraded] << "profile email must be set"
  end
end