2011-09-27 30 views
3

我正在處理的Rails 3.1應用程序中的一個模型具有「代碼」屬性,該屬性在創建記錄時自動生成並且必須是唯一的。應用程序應檢查數據庫以查看生成的代碼是否存在,如果存在,則應生成新代碼並重復該過程。確保應用程序級別的唯一列值

我可以通過add_index :credits, :code, :unique => true(我正在做的)以及validates_uniqueness_of的模型確保數據庫級別的字段唯一性,但如果生成的代碼存在,這兩種方法都會返回錯誤。我需要在重複的情況下再試一次。生成的代碼足夠長,重複是不太可能的,但我需要100%確定。

此代碼生成對最終用戶透明地處理,所以他們不應該看到錯誤。一旦生成代碼,檢查它是否存在並重復該過程直到找到唯一值的最好方法是什麼?

回答

3

這裏有一個簡單的例子,還有技術上的競爭條件在這裏,雖然除非你看到成百上千的每秒創建它真的不應該是擔心,最壞的情況是你的用戶得到一個uniquness錯誤如果兩個將創建以這樣一種方式,他們都執行查找和使用相同的URL

class Credit < ActiveRecord::Base 
    before_validation :create_code, :if => 'self.new_record?' 
    validates :code, :uniqueness => true 

    def create_code 
    self.code = code_generator 
    self.code = code_generator until Credit.find_by_code(code).nil? 
    end 
end 

如果您確實需要刪除其中兩個創建串聯運行的競爭條件的情況下返回零和兩個觸發找到運行相同的代碼並返回零,你可以用需要數據庫特定SQL的表鎖來封裝查找,或者你可以c reate一個表有一行用於鎖定通過悲觀鎖定,但我不會走得那麼遠,除非您期望每秒創建數百個,並且您絕對要求用戶永遠不會看到錯誤,這是可行的,只是一種在大多數情況下過度殺傷。

+0

啊,是的,直到。這應該做得很好,謝謝。創作率每兩週更接近一個,所以我不希望這裏發生任何碰撞。 – Kenn

0

我不確定是否有內置的方式。我一直使用before_create

這是一個UrlShortener上下文中的示例。

class UrlShortener < Activerecord::Base 
    before_create :create_short_url 

    def create_short_url 
    self.short_url = RandomString.generate(6) 
    until UrlShortener.find_by_short_url(self.short_url).nil? 
     self.short_url = RandomString.generate(6) 
    end 
    end 
end 
+0

我自己把它放在'before_validation'中。 – Eric

+0

before_create不會與唯一性驗證約束一起工作,因爲before_create回調將永遠不會被觸發。 before_validation選項起作用,只記得添加一個:if =>'self.new_record?'條款,所以它不會觸發更新 – Cluster