我的rails应用程序中有一个Player模型。我正在评估的2列是最高级别和最高分数。这是跨多个配置文件的单个玩家的统计信息跟踪,因此有可能这些值中的任何一个都可能低于db中的当前值。因此,我只希望它更新特定列,如果传入的发布值大于数据库中的值。阅读一些内置的验证选项,我无法像我预期的那样工作,但是,我能够编写自己的验证工作,但代价是调用Player.find(id)该模型。有没有办法解决这个问题,以便我的Player.update()不会同时导致UPDATE和SELECT?
这是我的模特:
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 :(得分:0)
当属性发生更改但记录尚未保留时,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