【问题标题】:How do you validate uniqueness of a pair of ids in Ruby on Rails?如何在 Ruby on Rails 中验证一对 id 的唯一性?
【发布时间】:2010-10-29 18:09:27
【问题描述】:

假设在 Ruby 中进行以下数据库迁移:

create_table :question_votes 做 |t| t.integer :user_id t.integer :question_id t.integer:投票 t.timestamps 结尾

进一步假设我希望数据库中的行包含唯一的 (user_id, question_id) 对。为实现这一目标,在模型中放入什么合适的灰尘?

validates_uniqueness_of :user_id, :question_id
似乎只是使行由用户 id 唯一,并且由问题 id 唯一,而不是由对唯一。

【问题讨论】:

  • 注意:我还没有回到这个项目,所以我没有时间测试下面的答案。如果有人发布了一个答案,该答案具有演示答案的最短测试以及该测试的输出,我将接受该答案。谢谢。

标签: ruby-on-rails validation model unique-constraint


【解决方案1】:

最好的方法是同时使用两者,因为通过唯一性验证时,rails 并不是 100% 可靠的。

你可以使用:

  validates :user_id, uniqueness: { scope: :question_id }

为了 100% 安全起见,请在您的数据库 (MySQL ex) 上添加此验证

  add_index :question_votes, [:user_id, :question_id], unique: true

然后你可以在你的控制器中使用:

  rescue ActiveRecord::RecordNotUnique

所以现在你是 100% 安全的,你不会有重复的值 :)

【讨论】:

【解决方案2】:

当您创建新记录时,这不起作用,因为您的父模型的 id 在验证时仍然不存在。

这应该对你有用。

class B < ActiveRecord::Base
  has_many :ab
  has_many :a, :through => :ab
end

class AB < ActiveRecord::Base
  belongs_to :b
  belongs_to :a
end

class A < ActiveRecord::Base
  has_many :ab
  has_many :b, :through => :ab

  after_validation :validate_uniqueness_b

  private
  def validate_uniqueness_b
    b_ids = ab.map(&:b_id)
    unless b_ids.uniq.length.eql? b_ids.length
      errors.add(:db, message: "no repeat b's")
    end
  end
end

在上面的代码中,我得到了所有b_id的参数集合,然后比较唯一值和获得的b_id之间的长度是否相等。
if are equals 表示没有重复的b_id

注意:不要忘记在数据库的列中添加唯一性

【讨论】:

    【解决方案3】:

    来自RailsGuidesvalidates 也可以:

    class QuestionVote < ActiveRecord::Base
      validates :user_id, :uniqueness => { :scope => :question_id }
    end
    

    【讨论】:

      【解决方案4】:

      如果使用mysql,你可以在数据库中使用唯一索引。是这样的:

      add_index :question_votes, [:question_id, :user_id], :unique => true
      

      当您尝试保存 question_id/user_id 的双重组合时,这将引发异常,因此您必须进行试验并找出要捕获和处理的异常。

      【讨论】:

      • 是的,但是当您只需要一条错误消息时,该异常无济于事。
      【解决方案5】:

      除了编写自己的验证方法之外,您可以使用validates_uniqueness_of 做的最好的事情是:

      validates_uniqueness_of :user_id, :scope => "question_id"
      

      这将检查 user_id 在与您尝试插入的记录具有相同 question_id 的所有行中是否唯一。

      但这不是你想要的

      我相信您正在寻找 :user_id:question_id 的组合在整个数据库中是唯一的。

      在这种情况下,你需要做两件事:

      1. 编写您自己的验证方法。
      2. 在数据库中创建约束 因为还有机会 您的应用程序将处理两条记录 同一时间。

      【讨论】:

      • 如果我这样做
         validate_uniqueness_of :user_id, :scope => :question_id validate_uniqueness_of :question_id, :scope => :user_id 
        够了吗?
      • 我没有看到使用 scopevalidates_uniqueness_of 和唯一的组合之间的区别。您能否举例说明它们的不同之处?
      【解决方案6】:
      validates_uniqueness_of :user_id, :scope => [:question_id]
      

      如果您需要包含另一列(或更多列),您也可以将其添加到范围中。示例:

      validates_uniqueness_of :user_id, :scope => [:question_id, :some_third_column]
      

      【讨论】:

      • 值应该在方括号中,而不是花括号中?
      • @gwho 方括号表示数组,值序列花括号表示散列,一组键值对。我们在这里没有指定键,只有值。我知道这有点晚了,你可能已经知道了,但它可能会帮助到这里的初学者:)
      • 哦,这不是一个作用域 lambda。
      • 如果您更喜欢validates 语法:validates :user_id, uniqueness: { scope: [:question_id] }
      • 只有当user_id and question_id 两者相同时,此验证才会失败?
      猜你喜欢
      • 1970-01-01
      • 2013-02-03
      • 1970-01-01
      • 2021-07-04
      • 1970-01-01
      • 2018-06-24
      • 1970-01-01
      • 2020-12-05
      • 1970-01-01
      相关资源
      最近更新 更多