【问题标题】:Ruby on Rails validating uniqueness on other column presenceRuby on Rails 验证其他列存在的唯一性
【发布时间】:2021-07-04 00:57:57
【问题描述】:

如何根据其他列是否有价值而不是实际内容来验证模型中的唯一性?范围似乎比较了第二列的内容。例如,我的用户模型列中有 email 和 project_id (都是字符串)等等。如果 project_id 为空或具有任何值,我想验证电子邮件是否唯一。使用范围允许创建对象 {email: 'a@a.a', project_id: nil}, {email: 'a@a.a', project_id: '1'}, {email: 'a@a.a', project_id: '2'} 等等。我想限制电子邮件的唯一性,以便前两个对象成为可能(project_id 为 nil 或 1 具有相同的电子邮件),最后一个对象会抛出错误“电子邮件已被使用”,因为当 project_id 具有值时,已经有用户使用相同的电子邮件.是否有适当的 Rails 方法来实现这一点,还是我需要编写一些自定义验证?

当然,我也有更好的电子邮件验证,不会接受'a@a.a',这只是一个例子:)

【问题讨论】:

  • 这能回答你的问题吗? Rails - Validation :if one condition is true
  • 不完全是因为这只是在满足条件时验证存在。我需要始终根据其他列的存在来验证唯一性。基本上,一封电子邮件最多允许 2 次,并且 project_id 为 nil 或具有任何值。
  • 我认为这里最简单的事情是有一个“空”项目,用于将您的 project_id 评估为 nil 的目的。这样您就可以在唯一性验证中使用标准范围。

标签: ruby-on-rails validation


【解决方案1】:

您可以创建您的自定义验证并使用validates_with 调用它,在您的情况下,它类似于:

class ExampleEmailUniqueness < ActiveModel::Validator
  def validate(record)
    error_message = "has already been taken"
    if record.project_id.nil? && Example.where(project_id: nil, email: record.email).exists?
      record.errors.add :email, error_message
    elsif Example.where(email: record.email).where.not(project_id: nil).exists?
      record.errors.add :email, error_message
    end
  end
end

class Example < ApplicationRecord
  validates_with ExampleEmailUniqueness
end

针对您的场景的验证将产生结果:

Example.new(email: 'a@a.a').save                  # Saved
Example.new(email: 'a@a.a', project_id: '1').save # Saved
Example.new(email: 'a@a.a', project_id: '2').save # Email has already been taken
Example.new(email: 'a@a.a').save                  # Email has already been taken

请注意,在保存一个不是 nil 的模型之后保存具有 nil project_id 的模型仍将通过验证并保存到数据库中,我不确定这是否是预期的行为。

【讨论】:

  • 而不是 empty? 将实例化所有示例对象使用 exists? 它将运行一个简单的 EXISTS 查询并返回一个布尔值而不将对象加载到内存中,例如Example.where(project_id: nil, email: record.email).exists?
  • @engineersmnky 你说得对,我编辑了我的代码以使用 if exists? 而不是 unless empty? 并将其简化为单个 if-elsif 语句,所以现在它看起来也更干净了
【解决方案2】:

唯一性验证中的范围选项与 Rails 范围不同。它与 SQL 更相关,通常仅限于属性名称。

因此,由于您不能在唯一性验证中使用自定义范围,因此您必须为此编写自定义验证。

validate :validate_email_on_project_id_existence  

def validate_email_on_project_id_existence
  if User.where(email: self.email).where(self.project_id.nil? ? 'project_id IS NULL' : 'project_id IS NOT NULL').first
    errors.add(:email, "some error")
  end
end

【讨论】:

  • 我很确定自定义验证是唯一的选择,而且这段代码比我想出的更简洁:)
猜你喜欢
  • 1970-01-01
  • 2013-02-03
  • 1970-01-01
  • 2010-10-29
  • 1970-01-01
  • 2018-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多