【发布时间】:2016-04-22 15:59:14
【问题描述】:
代码
# Item Model
class Item < ActiveRecord::Base
attr_accessor :paid_amount
after_save :amount_processed?
def amount_processed?
if self.try(:paid_amount)
return true
else
return false
end
end
end
# Controller snippet
...
# params = {"paid_amount" => 10}
@item.assign_attributes(params)
if @item.valid?
@item.save
end
...
当前回调未运行,即代码从不检查amount_processed?。发生这种情况的原因是因为paid_amount 不是Item 的数据库属性。但这是设计使然。问题是假设这个设计会保留,我是否有办法运行回调来检查amount_processed? 仅基于属性已传递的事实? (即,如果你运行@item.paid_amount,你会在@item.assign_attributes之后得到"10")。
请注意,以下回调将不起作用:
-
after_save或after_touch因为如上所述,paid_amount永远不会保存,所以@item永远不会更新 -
after_find因为根据定义,它在属性分配之前运行。所以通过这个验证,即使amount_processed?被选中,当它被选中时,@item.paid_amount = nil
希望将两者结合起来......
由于问题询问如何根据当前设计进行此操作,因此完全可以接受的答案是说在当前设计中,这是不可能的。只有当属性实际更新时,回调才会起作用。在这种情况下,我已经有两种策略来解决这个问题,其中最简单的是将amount_processed? 移动到控制器级别,这样我就可以在assign_attributes 之后检查paid_amount。另一种策略是有一个子项,但这取决于有关代码的其他信息,为简单起见,我保留了这些信息。
谢谢!
【问题讨论】:
-
为什么不直接使用
validate :amount_processed?。见custom validations -
很确定您的代码应该可以正常工作。您确定 (a) params 包含您认为它所做的并且 (b) 该实体实际上是有效的吗?如果您直接从表格或其他形式获得
params,我认为您需要从params[:item]获取值afterSave仅仅是一个回调,它独立于任何变量。您可以抓住对象并立即调用save,然后将调用afterSave。 -
@GoGoCarl
after_save不起作用的原因是paid_amount不是Item的数据库属性,它只是我可以访问的东西(因为attr_accessor用户通过通过参数)。所以换句话说,如果我这样做@item.update_attributes({"paid_amount"=> "10"}),回调不会运行,因为实际上没有任何更新 -
你上面的代码是
assign_attributes,这有很大的不同。 -
呃,我收回了。应该还能工作。
update_attributes的核心实际上只是一条捷径。检查valid?的结果
标签: ruby-on-rails activerecord callback