如果另一个字段为空,则验证字段是否存在 - Rails

70

我有一个表单,其中包含一个手机号码/移动电话号码和一个家庭电话号码。

如果电话号码留空或反之,则只想验证手机号码/移动电话号码的存在性。

这些字段的当前验证如下所示。

validates_presence_of :mobile_number
validates_presence_of :home_phone

validates_length_of :home_phone, :minimum => 12, :maximum => 12
validates_length_of :mobile_number, :minimum => 10, :maximum => 10, :allow_blank => true

validates_format_of :home_phone, :with => /\A[0-9]{2}\s[0-9]{4}\s[0-9]{4}/, :message => "format should be 02 9999 9999"

我以为我可以做出以下类似的东西,但不确定如何确切地实现。

validates_presence_of :mobile_number, :unless => :home_phone.blank?

我正在使用 Rails 3。

7个回答

136

你不需要使用lambda表达式。这个代码就可以:

validates_presence_of :mobile_number, :unless => :home_phone?
此外,所有验证器都具有相同的if/unless选项,因此您可以根据需要将它们设为有条件的。
更新:几天后回顾这个答案,我发现我应该解释一下为什么它能够工作:
- 如果您将验证器的:unless选项设置为一个符号,Rails将查找该名称的实例方法,并在验证时调用该名称的方法。只有在方法返回false时才执行验证。 - ActiveRecord会自动为您模型的每个属性创建一个问号方法,因此您模型的表中存在home_phone列会导致Rails创建一个方便的#home_phone?方法。如果home_phone存在(即不为空),则此方法返回true。如果home_phone属性为nil或空字符串或一堆空白,则home_phone?将返回false。
更新:确认这种旧技术在Rails 5中仍然有效。

我编辑了这个答案,以符合我在使用Apache/Passenger和Rails 3时的经验。应用程序甚至拒绝使用 :unless => :home_phone? 启动,我的理论是方法查找发生得太早了。 - benzado
1
不要进行未明确归属的大幅编辑。请在评论或另一个答案中解释您的不同经验。我已经还原了您的一些更改并归属了其余部分。 - Rob Davis
3
谢谢您的帮助。我只是想说:不要通过添加lambda表达式来编辑以“你不需要lambda”开头的答案。这是没有意义的,也不是我写的。另一个答案使用了lambda表达式,您可以投票支持它并降低对我的支持,或发表相反的评论。这个答案对我有用,就像它原本的样子一样,所以我不确定为什么在您的情况下它不起作用。 - Rob Davis
如果 home_phone 是一个布尔值,会怎样? - zx1986
这种方法的问题在于,对于整数值为0的情况,检查结果将为真。 - Jared
显示剩余4条评论

23

你必须使用一个lambda / Proc对象:

validates_presence_of :mobile_number, :unless => lambda { self.home_phone.blank? }

我认为你的意思是使用 :if 而不是 :unless(或者使用 #present? 而不是 #blank?)。此外,lambda 表达式并不是必需的。 - Rob Davis
@RyanBigg,是的,lambda可以,但我问Paul为什么我的基于符号的答案不起作用。 - Rob Davis
因为 home_phone 可能返回一个空字符串,我认为这会使验证通过。我认为布尔方法只检查 nil - Ryan Bigg
不,如果“home_phone”属性为空(nil或空字符串),则“home_phone?”(带问号)为false。我想知道Paul是否意外地忘记了问号。那将是关键的。 - Rob Davis
@RobDavis - 当然没问题!我也非常感兴趣。这是可行的代码:validates_presence_of :string_value, unless: lambda {self.integer_value.present? || self.decimal_value.present?}(无法在评论中放置正确的代码)。这些是不可行的代码:validates_presence_of :string_value, unless: :integer_value? || :decimal_value?validates_presence_of :string_value, unless: (:integer_value? || :decimal_value?) - silverdr
显示剩余3条评论

9

从Rails 4开始,您可以将块传递给presence方法。简而言之:

validates :mobile_number, presence: {unless: :home_phone?}

此外,:home_phone? 对于 nil 或空值返回 false。

这个可以工作,但我要指出的是你传递了一个 unless: :home_phone? 的哈希,而不是一个代码块(它应该是 { !home_phone? })。 - Mirror318

6
在Rails 7中测试过,这个功能完美运行:
validates :mobile_number, presence: { unless: :home_phone }

4

以下是针对Rails 4的另一种可行方式:

  validates_presence_of :job, if: :executed_at?

  validates :code,
            presence: true,
            length:  { minimum: 10, maximum: 50 },
            uniqueness: { case_sensitive: false },
            numericality: { only_integer: true }

1
一个简短的解决方案:

validates_presence_of :mobile_number, unless: -> { home_phone.blank? }

0
在新版本的Rails中,不再依赖于旧的validates_presence_of,而应该使用validates并列出每个属性的验证。
validates :mobile_number, presence: { if: -> { home_phone.present? } }


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接