55

假设在 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 唯一,而不是由对唯一。

4

6 回答 6

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

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

validates_uniqueness_of :user_id, :scope => [:question_id, :some_third_column]
于 2009-05-29T00:12:40.747 回答
27

如果使用 mysql,您可以使用唯一索引在数据库中执行此操作。它是这样的:

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

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

于 2010-06-24T08:27:31.807 回答
17

最好的方法是同时使用两者,因为当唯一性验证通过时,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% 安全的,你不会有重复的值 :)

于 2015-02-06T12:01:14.037 回答
15

来自RailsGuidesvalidates也可以:

class QuestionVote < ActiveRecord::Base
  validates :user_id, :uniqueness => { :scope => :question_id }
end
于 2012-08-16T07:13:09.590 回答
10

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

validates_uniqueness_of :user_id, :scope => "question_id"

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

但这不是你想要的

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

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

  1. 编写自己的验证方法。
  2. 在数据库中创建一个约束,因为您的应用仍然有可能同时处理两条记录。
于 2009-05-29T00:14:09.480 回答
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

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

于 2016-07-15T17:41:13.457 回答