2012-04-10 23 views
1

以下是我正在嘗試執行的操作;我認爲這是一個普遍的問題,但不知何故我找不到任何相關的主題...Rails 3:處理ActiveRecord模型中數據庫適配器的異常

我有一個範圍的唯一性約束模型。我已經決定在遷移上的表定義唯一索引,像這樣做:

class CreateLossRatios < ActiveRecord::Migration 
    def up 
    ... 
    add_index :loss_ratios, [ :tool_id, :ends_at ], :unique => true 
    end 

    def down 
    ... 
    end 
end 

這讓試圖保存記錄違反指數獨特的ActiveRecord時拋出異常。現在我想讓它看起來是一個驗證錯誤。我認爲最好的方法是在LossRatio模型中捕獲ActiveRecord :: RecordNotUnique權限,並用有意義的消息填充錯誤散列。我這樣做了:

class LossRatio < ActiveRecord::Base 
    belongs_to :tool 

    validates :rate, :ends_at, :tool, :presence => true 
    validates_numericality_of :rate 
    validates_inclusion_of :rate, :in => (0..1) 

    %w{ create save }.each do |name| 
    %W{ #{name} #{name}! }.each do |method| 
     define_method(method) do |*args| 
     begin 
      super(*args) 
     rescue ActiveRecord::RecordNotUnique => ex 
      self.errors.add(:ends_at, I18n.t('activerecord.errors.models.loss_ratio.attributes.ends_at.not_unique')) 
     end 
     end 
    end 
    end 

end 

這工作,但似乎有點麻煩。我知道我在這裏做假設(即如果我添加另一個數據庫級別唯一性約束等會發生什麼情況),但我沒有看到解決方法。處理這種情況時是否有更優雅的解決方案/最佳做法? 一種選擇,我能想到的是使用rescue_from,但我不想這樣做,因爲

  • 我不認爲這個邏輯控制器中的屬於,我希望把它透明的應用程序邏輯
  • 最有可能不會有關聯的控制器(這些對象將僅作爲通過另一個模型的關聯而創建),這使得從我的角度來看它更加錯誤。

有沒有辦法讓這個模型從任何實例方法拋出的異常救援?我試過使用一個類級別的救援條款,但它沒有捕獲任何東西。


另一個問題是,我是否仍然使用AR範圍驗證的ends_at。即使RecordNotUnique處理完畢,對象仍會認爲自己有效,並在嘗試保存失敗後設置其時間戳。它可以導致任何不需要的副作用嗎?

+0

除了我下面的評論,你不應該'保存'從不提出任何例外。它只是返回true或false。 – 2012-04-11 06:18:28

+0

具體來說,在這種情況下,當通過DB約束繞過ActiveRecord進行驗證時,RecordNotUnique也由non-bang方法引發。 – HargrimmTheBleak 2012-05-04 08:12:04

回答

1

爲了解決這個問題,驗證你可以嘗試

validates_uniqueness_of :ends_at, :scope => :tool_id

+0

當創建一個工具時,這不起作用,因爲在執行驗證時,這個實例不會保存到數據庫,所以它沒有一個id,也沒有可以驗證的範圍。所以這隻適用於更新操作。我實際上遇到了許多不同的問題:(我會盡快發佈一個完整的解決方案... – HargrimmTheBleak 2012-05-04 09:16:39

2

還應驗證模型中的獨特性(如提出gmalette)。這樣,您可以在遇到數據庫之前獲取大多數錯誤。雖然它花費你額外SELECT它確保你有一個實際的驗證。

有了這個,數據庫索引應該只在兩個獨立進程嘗試幾乎同時插入衝突數據時解決競爭條件。我通常會通過發出錯誤消息來要求用戶再次嘗試來處理這些錯誤。

選擇性處理數據庫錯誤並不是一個好主意,因爲這本身通常很容易出錯。相反,儘量在ruby層中處理儘可能多的驗證,並將數據庫層僅用作安全網。

+0

好吧,這回答我的第二個問題(嗯,大部分),但帶來了另一個。我將如何測試數據庫適配器異常部分,並進行驗證?validates_uniqueness_of語句會在保存時使異常消失,那麼,我是否應該以某種方式人爲地導致競爭條件?我該怎麼做?您的最後一次聲明意味着處理數據庫異常不是一個好習慣,就像我試圖做的那樣?任何示例爲什麼? – HargrimmTheBleak 2012-04-10 14:41:58

+0

您可以使用'model.save(false)'來禁用所有驗證。至於爲用戶處理它,我想這些錯誤是非常罕見的,並且可以同時擁有許多*源,解決方案總是要麼再試一次,要麼改變一些東西然後再試一次,這取決於用戶的決定。處理(如果有的話)是爲了創造更好的錯誤消息。但說實話,我通常有更大的可用性問題,比改善這些錯誤:) – 2012-04-11 06:16:38

+0

對,不知何故,我忘了:validate => false選項......謝謝! – HargrimmTheBleak 2012-04-12 15:39:16

相關問題