Rails 4 before_update条件对单个列的SQL开销

我的rails应用程序中有一个Player模型。 我正在评估的2列是最高级别和最高分数。 这是跨多个配置文件的单个玩家的统计数据跟踪,因此有可能这些值中的任何一个都可能低于数据库中的当前值。 因此,我只希望它更新特定列,如果传入的发布值大于数据库中的值。 阅读一些内置的validation选项,我无法像我想的那样工作,但是,我能够编写自己的validation工作,但代价是调用Player.find(id)该模型。 有没有办法解决这个问题,以便我的Player.update()不会同时导致UPDATE和SELECT?

这是我的模型:

class Player  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寻找更大更好的鼠标陷阱

当属性发生更改但记录尚未保留时,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