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_was
和highestScore_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