Rails未触发我的after_commit回调函数。

10

你好,我有一个奇怪的问题。我使用 after_commit 回调函数来发送通知,但是似乎这个回调根本没有触发。简化后的情况看起来像这样:

class Message < ActiveRecord::Base
  after_commit :do_something

  def do_something
    raise 'Doing something'
  end
end

当我打开控制台并创建一条消息时,我期望能看到增加操作。但是什么都没有发生。此外,即使我完全删除“do_something”方法,Rails 也不会抱怨。请注意,这不是使用事务性固件进行的测试。我甚至在数据库中看到了记录提交。我的 Rails 版本是 3.0.9。谢谢任何帮助,特别是如果它很好 :-)

编辑:后来我发现在将代码部署到另一台机器时触发了回调。所以肯定是我的设置有问题。但我仍然感激您对可能导致此问题的原因的洞察力。

编辑2:从评论中得知:

  • 数据库是 MySQL,因此存在事务。
  • 指定回调函数的动作没有帮助(:on => :create)。
  • 我需要使用 after_commit 而非其他回调函数。

你是如何在控制台中创建模型的? - felipeclopes
我尝试过通过工厂以及保存和保存!调用来实现它。 - Renra
据我理解,它们都应该触发回调。我也在生产控制台上尝试过。 - Renra
尝试在after_commit中登录而不是raising。Rails会从任何被调用的after_commit钩子中拯救出nil。 - gravitron
这不是你的错,而是Rails中的一个错误。 - David
6个回答

7

可能的原因之一(至少在5.0上)是:

after_commit :do_something, on: :create
after_commit :do_something, on: :destroy

这里do_something被引用了两次,但都不会被执行。一旦它们被合并成

after_commit :do_something, on: [:create, :destroy]

它将被再次调用。


对于4.0版本,最后声明的after_commit将会生效。在这种情况下是destroy。 - dan

7

David在问题评论中提到了这种行为的解释。

交易回调文档

“如果其中一个回调引发异常,它们将被忽略,以免干扰其他回调。因此,如果您的回调代码可能会引发异常,您需要在回调内部进行救援并适当地处理它。”

另请参见:


3
您可能需要在spec/support中打补丁,以启用使用事务性夹具时的after_commit回调。如果您正在使用事务性夹具,则实际上从未发生提交。
打补丁的方法如下:https://gist.github.com/charleseff/1305285

2
这个Gist已经被打包成宝石并且也在工作 https://github.com/grosser/test_after_commit - Ev Dolzhenko

0

当使用 after_commit 时,您应该指定触发操作的时间...

after_commit 基本上用于那些存在事务概念的数据库...因此,请尝试以下代码...

class Message < ActiveRecord::Base
 after_commit :do_something, :on => [:create, :update, :destroy]

 def do_something
  raise 'Doing something'
 end
end

也是这么想的,但是没有帮助。 - Renra
对不起,我忘了提到数据库是MySQL。 - Renra
我认为这应该不会有太大的影响,因为MySQL和SQLite都支持事务,如果它在MySQL上无法运行,那么它也不会在SQLite上运行...但你仍然可以尝试一下,让我知道结果... - Aditya Kapoor

0

尝试一下下面的代码是否有帮助 -

class Message < ActiveRecord::Base
 after_commit :do_something, :only => [:create, :update, :destroy]

 def do_something
  raise 'Doing something'
 end
end

不,这并没有帮助。请查看问题的第二次编辑。但还是谢谢。 - Renra

-2

你有以下内容:

class Message < ActiveRecord::Base
  after_commit :do_something

  def do_something
    puts 'Doing something'
  end
end

根据您的需求,您可以尝试使用after_createafter_save

after_commits仅在记录成功保存到数据库时执行,因为savesave!destroy都在事务中运行。

如文档所述:Active Record Callbacks


4
我需要使用 after_commit,因为 after_create 可能会在记录实际提交到数据库之前执行,使得其他连接也可以读取它。如果你发送一个带有记录ID的通知到其他设备,而该记录尚未存在,则什么也不会发生。请注意,这里强调了“提交(commit)”与“创建(create)”之间的差异。 - Renra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接