2009-05-28 85 views
48

假设在红宝石下面的DB迁移:你如何验证Ruby on Rails中一对id的唯一性?

 
    create_table :question_votes do |t| 
     t.integer :user_id 
     t.integer :question_id 
     t.integer :vote 

     t.timestamps 
    end 

进一步假设,我希望在DB行包含唯一的(USER_ID,question_id)对。什么是正确的尘埃投入模型来实现这一目标?

validates_uniqueness_of :user_id, :question_id
似乎只是简单地让用户ID唯一的行,并通过问题id唯一,而不是唯一的一对。

+1

注:我还没有回到这个项目,所以我没有时间来测试下面的答案。如果有人发布了答案最短的答案,以及该答案的输出结果,我会接受该答案。谢谢。 – dfrankow 2009-12-12 19:18:42

回答

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

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

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

值应该放在方括号中,而不是大括号? – ahnbizcad 2014-08-28 11:24:40

+3

@gwho **方括号**表示**数组,一系列值**。 **大括号**表示一个**散列,一组键值对**。我们在这里没有指定键,只有值。我知道这有点晚了,你可能已经知道了,但它可能会帮助初学者来到这里:) – 2014-11-07 14:40:41

+0

哦,它不是一个范围lambda。 – ahnbizcad 2014-11-08 01:18:58

10

除了写自己的validate方法,你可以用validates_uniqueness_of做的最好的是这样的:

validates_uniqueness_of :user_id, :scope => "question_id" 

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

但这不是你想要的

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

在你需要做两件事情的话:

  1. 写自己的validate方法。
  2. 在数据库 中创建一个约束,因为您的应用程序仍有可能在同一时间处理 两条记录。
25

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

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

这将引发异常,当您尝试保存question_id/USER_ID的翻番式组合,让你不得不尝试并找出捕获并处理该异常。

15

RailsGuidesvalidates工程太:

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

最好的办法就是同时使用,因为轨道是不是100%可靠的时候唯一的验证来通。

您可以使用:

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

,并为安全起见100%,在你的数据库添加此验证(MySQL的前)

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之间的长度是平等的。
如果等于意味着没有重复b_id

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