2009-05-28 84 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

注意:不要忘記在數據庫的列中添加唯一的