有没有可能避免这种行为,比如在执行期间禁用回调函数?或者还有其他方法吗?
谢谢!
解决方法是在类中设置一个变量,在 after_save 中检查其值。
这样,它只会尝试保存两次。这很可能会对您的数据库进行两次访问,这可能是需要的,也可能不需要。
我模糊地感觉到有内置的解决方案,但这是一种相当可靠的方式,可以防止几乎任何应用程序中特定递归点的递归。我还建议再次查看代码,因为很可能您在 after_save 中执行的任何操作都应该在 before_save 中完成。虽然这样做不是总是正确的,但情况比较少见。
我没有看到这个回答,所以我想添加一下,以防有人在搜索此主题时需要帮助。(ScottD的without_callbacks建议接近解决方法。)
ActiveRecord为此情况提供了update_without_callbacks
,但它是一个私有方法。使用send方法获得访问权限。正是因为在保存对象的回调内部,所以才要使用它。
另外,在这里还有一个很好地涵盖了此问题的SO线程: 如何避免运行ActiveRecord回调?
你可以使用before_save回调函数吗?
另外,您还可以查看插件Without_callbacks。它为AR添加了一个方法,让您可以跳过给定块的某些回调。
例如:
def your_after_save_func
YourModel.without_callbacks(:your_after_save_func) do
Your updates/changes
end
end
def before_save
@attempted_save_level ||= 0
@attempted_save_level += 1
end
def after_save
if (@attempted_save_level == 1)
#fill in logic here
save #fires before_save, incrementing save_level to 2, then after_save, which returns without taking action
#fill in logic here
end
@attempted_save_level -= 1 # reset the "prevent infinite recursion" flag
end
这个技巧就是使用#update_column
:
此外,它只向数据库发出单个快速更新查询。
http://apidock.com/rails/ActiveRecord/Persistence/update_columns
谢谢大家,问题在于我也更新了其他对象(如果你愿意称其为兄弟对象的话)…忘了提到这一点…
所以在保存之前进行操作已经不可行了,因为如果保存失败,所有对其他对象的修改都必须被撤回,这可能会变得混乱 :)
我也遇到了这个问题。我需要保存一个依赖于对象ID的属性。我通过使用回调的条件调用来解决了这个问题...
Class Foo << ActiveRecord::Base
after_save :init_bar_attr, :if => "bar_attr.nil?" # just make sure this is false after the callback runs
def init_bar_attr
self.bar_attr = "my id is: #{self.id}"
# careful now, let's save only if we're sure the triggering condition will fail
self.save if bar_attr
end