如何避免循环循环

时间:2016-07-04 23:56:51

标签: ruby-on-rails ruby ruby-on-rails-4 callback

我想我在这里很密集,因为我不断收到stack too deep错误......

我有一个Child和一个Parent关系对象。我想要做两件事:

  • 如果您尝试更新Child,则无法将其status_id更新为1,除非它具有Parent关联
  • 如果您创建Parent然后将其附加到Child,则Child的状态应自动设置为1

以下是Parent关联的添加方式:

parent = Parent.new
if parent.save
  child.update_attributes(parent_id:1)
end

我在Child模型上有这些回调:

  validate :mark_complete
  after_update :set_complete

  # this callback is here because there is a way to update the Child model attributes
  def mark_complete
    if self.status_id == 1 && self.parent.blank?
      errors[:base] << ""
    end
  end

  def set_complete
    if self.logistic.present?
      self.update_attribute(:status_id, 1)
    end
  end

上面的代码实际上并不那么有效,因为它理想情况下它会是1分贝命中,一次完成。但是我发现它太过于脑力不足以弄清楚为什么......我不确定为什么它甚至不能正常工作,因此甚至无法开始考虑使这成为一个单一的数据库事务。

示例

希望这有助于澄清。想象一下Charge模型和Item模型。每个Item都有一个ChargeItem也有一个属性paid。两件事:

  • 如果您更新Item,则在paidtrue对象相关联之前,您无法将Item更新为Charge / p>

  • 如果您通过更新Charge上的Item属性将charge_id对象与Item相关联,那么代码可以节省您的时间并自动设置paidtrue

3 个答案:

答案 0 :(得分:1)

我发现有很多让我感到困惑的事情,但在我看来,你在:set_complete之后调用after_update并且在set_complete内你正在更新属性,因此你似乎有一个永久循环。可能还有其他循环,我看不到,但那个循环对我来说很突出。

答案 1 :(得分:0)

避免像这样的循环递归情况的一种方法是提供标志作为参数(或其他),以阻止循环继续。

在这种情况下,(虽然我完全不确定这个案例)我认为你可以提供一个标志来表明通话的来源。如果更新的来源是附加的费用,则传递一个标志,该标志将停止检查发生或修改它以防止发生循环。对于这种情况,也许是第二组逻辑?

答案 2 :(得分:0)

在使用ActiveRecord回调时,我在一段时间内遇到了stack level too deep问题。 在我的情况下,问题出现在update_attribute更新通过回调后,即在您的情况下再次调用set_complete,其中update_attribute将再次被触发,并且这将无休止地重复。 / p>

我通过使用update_column而不是触发任何回调或验证来解决这个问题,但设置标志是更经常在线建议的。

此时我没有减少数据库写入操作的答案,如果我能想到任何事情,我会添加这个答案。

希望这有帮助