验证另一个属性存在范围内的属性的唯一性

Validate uniqueness of attribute scoped by another attribute presence

如果我需要验证所有者范围内的猫名称的唯一性,以便任何所有者都不能拥有许多同名的猫,我可以使用它:

validates_uniqueness_of :name, scope: :owner_id

但是我如何验证猫是否拥有主人的唯一性?

这样所有有主人的猫都有唯一的名字。

所有没有主人的猫都有唯一的名字。

所以它的范围类似于 scope: self.owner.present?

我可以在不使用自定义验证方法的情况下使用验证助手吗?

您可以为唯一性验证分配一个 conditions 子句。请参阅 the documentation,其中给出了这个示例:

class Article < ActiveRecord::Base
  validates_uniqueness_of :title, conditions: -> { where.not(status: 'archived') }
end

所以如果你想确保所有没有主人的猫的唯一性,你会写

validates_uniqueness_of :name, conditions: -> { where(owner_id: nil) }

或者,如果您想确保拥有主人的所有猫的名字都是唯一的,而不管它们的主人是谁:

validates_uniqueness_of :name, conditions: -> { where.not(owner_id: nil) }

如果您只想做其中之一,那也行。但是,如果你想同时执行这两个操作,你会发现两个条件都被检查了 - 实际上,你返回了一个唯一性约束来检查整个 table,但在两个查询而不是一个查询中。

所以,如果我们想要两个检查,我们需要确保只检查其中一个,我们可以用if/unless 修饰符:

validates_uniqueness_of :name, conditions: -> { where(owner_id: nil) },
    if: -> { owner_id.nil? }
validates_uniqueness_of :name, conditions: -> { where.not(owner_id: nil) },
    unless: -> { owner_id.nil? }

所以现在,如果您的猫有所有者 ID,则在具有所有者 ID 的所有记录范围内检查唯一性的验证将 运行。但是,如果它没有所有者 ID,则会启动其他验证。

这意味着要多跳几圈,但应该可以满足您的需要。