0

tI在我的Rails應用程序中有一個Player模型。我正在評估的2列是highestLevel和highestScore。這是一個跨多個配置文件跟蹤單個玩家的統計數據,因此這些值中​​的任何一個都可能低於db中的當前值。因此,如果傳入的發佈值大於數據庫中的發佈值,我只希望它更新特定列。閱讀一些內置的驗證選項,我無法按照我的意圖工作,但是,我能夠編寫自己的驗證工作,但是以調用Player.find(id)爲代價該模型。有沒有辦法解決這個問題,這樣我的Player.update()不會導致UPDATE和SELECT?Rails 4個別列上的before_update條件SQL開銷

這裏是我的模型:

class Player < ActiveRecord::Base 
    #validates_numericality_of :highestLevel, greater_than: Proc.new { |r| r.highestLevel } 
    #validates_numericality_of :highestScore, greater_than: Proc.new { |r| r.highestScore } 

    before_update :player_record, :eval_highestLevel, :eval_highestScore 

    # TODO: Find a more effective way to handle update evaluations with less SQL overhead 
    private 
    def eval_highestLevel 
     # if highestLevel in DB has higher value , update the value 
     if @p.highestLevel > self.highestLevel 
     self.highestLevel = @p.highestLevel 
     end 
    end 

    def eval_highestScore 
     # if record in DB has higher value , update the value 
     if @p.highestScore > self.highestScore 
     self.highestScore = @p.highestScore 
     end 
    end 

    def player_record 
     @p = Player.find(id) 
    end 
end 

如何使這個更高效的任何想法,或者我應該息事寧人?我一直在爲Rails 4.x尋找更大,更好的鼠標陷阱

+0

豈不'@ p'和'self'是一回事嗎?還是說很多東西都與背後的'highestScore'搞混了,所以你需要把它從數據庫中取出來看看你是否需要改變它?如果是這樣,那麼正確的解決方案將使用觸發器將該邏輯放入數據庫中,以便更容易避免競爭條件。 – 2014-09-05 17:17:32

回答

0

當屬性發生變化但尚未保存記錄時,Rails會自動定義助手以獲取屬性的先前值。它們被命名爲例如attribute name_was,所以在這種情況下Player#highestLevel_washighestScore_was

def eval_highestLevel 
    # if highestLevel in DB has higher value , update the value 
    if self.highestLevel_was > self.highestLevel 
    self.highestLevel = @p.highestLevel 
    end 
end 

這在ActiveModel::Dirty記錄。許多其它有用的方法被定義,例如:

  • attribute_name_changed?返回true如果屬性已經改變。
  • attribute_name_change返回一個包含兩個元素(舊值和新值)的數組。

有了這些知識,我們實際上可以簡化您的回調了很多:

class Player < ActiveRecord::Base 
    before_update :ensure_highestLevel, if: :highestLevel_changed? 
    before_update :ensure_highestScore, if: :highestLevel_changed? 

    protected 
    def ensure_highestLevel 
    self.highestLevel = self.highestLevel_change.compact.max 
    end 

    def ensure_highestScore 
    self.highestScore = self.highestScore_change.compact.max 
    end 
end 

由於self.highestScore_change將兩個數字的數組,我們可以調用max獲得較高的一個。我們使用Array#compact,因爲如果舊值或新值是nil,我們會得到一個錯誤([nil,1].max # => ArgumentError: comparison of NilClass with 1 failed)。 compact首先從陣列中刪除任何nil

甚至更​​簡潔地說:

class Player < ActiveRecord::Base 
    before_update ->{ highestLevel = highestLevel_change.compact.max }, 
    if: :highestLevel_changed? 

    before_update ->{ highestScore = highestScore_change.compact.max }, 
    if: :highestScore_changed? 
end