如果我將一個after_save回調添加到ActiveRecord模型,並在該回調中使用update_attribute更改對象,則會再次調用回調,並且所以發生了「堆棧溢出」(呵呵,無法抗拒)。使用after_save回調修改同一對象而不觸發回調(遞歸)
是否有可能避免這種行爲,也許可以在執行期間禁用回調?還是有另一種方法?
謝謝!
如果我將一個after_save回調添加到ActiveRecord模型,並在該回調中使用update_attribute更改對象,則會再次調用回調,並且所以發生了「堆棧溢出」(呵呵,無法抗拒)。使用after_save回調修改同一對象而不觸發回調(遞歸)
是否有可能避免這種行爲,也許可以在執行期間禁用回調?還是有另一種方法?
謝謝!
一種解決方法是在類中設置一個變量,並在after_save中檢查其值。
通過這種方式,它只會嘗試保存兩次。這可能會打擊你的數據庫兩次,這可能會或可能不需要。
我有一種模糊的感覺,即有內置的東西,但這是一種相當簡單的方法來防止任何應用程序中的遞歸特定點。 我還建議再次查看代碼,因爲無論你在after_save中做什麼都應該在before_save中完成。有時候這不是真的,但它們相當罕見。
看看如何實現update_attribute。使用send方法代替:
send(name.to_s + '=', value)
你可以使用before_save回調嗎?
如果使用before_save,則可以在保存完成之前修改任何其他參數,這意味着您不必顯式調用save。
謝謝你們,問題是,我更新其他對象了(如果你願意的兄弟姐妹)...忘了提,部分...
所以before_save是不可能的,因爲如果保存失敗所有修改到其他對象將不得不恢復,並可能變得凌亂:)
此代碼甚至不嘗試解決線程或併發問題,就像Rails正常。如果您需要該功能,請注意!
基本上,這個想法是保持計數在什麼水平的遞歸調用「保存」你是,只有當after_save退出最高水平時才允許。你也想添加異常處理。
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
這很聰明,謝謝! – Ivan 2008-10-20 22:35:02
你也可以看看插件Without_callbacks。它向AR添加了一種方法,可以讓您跳過給定塊的某些回調。 例如:
def your_after_save_func
YourModel.without_callbacks(:your_after_save_func) do
Your updates/changes
end
end
不知道該插件,它會派上用場,謝謝! – Ivan 2008-10-20 22:39:27
我也有這個問題。我需要保存一個取決於對象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
我沒有看到這個答案,所以我想如果它有助於任何有關這個主題的搜索我會添加它。 (ScottD的without_callbacks建議很接近。)
ActiveRecord爲這種情況提供了update_without_callbacks
,但它是一種私有方法。無論如何請使用send來訪問它。在你保存的對象的回調中,正是使用它的原因。
而且還有另外一個SO線程這裏覆蓋此相當不錯: How can I avoid running ActiveRecord callbacks?
有時候,這是因爲在模型不指定attr_accessible的。當update_attribute想要編輯屬性時,如果發現它們不可訪問並且創建新的對象。在保存新對象時,它將進入一個無止境的循環。
我有一個需要在文本塊gsub
路徑名時,它的記錄被複制到不同的環境:
attr_accessor :original_public_path
after_save :replace_public_path, :if => :original_public_path
private
def replace_public_path
self.overview = overview.gsub(original_public_path, public_path)
self.original_public_path = nil
save
end
鍵停止遞歸是分配從屬性值和然後將該屬性設置爲零,以便在隨後的保存中不滿足:if
條件。
訣竅只是使用#update_column
:
此外,它只是簡單地向數據庫發出一個快速更新查詢。
http://apidock.com/rails/ActiveRecord/Persistence/update_columns
可以在關聯使用after_save
與if
如下:
after_save :after_save_callback, if: Proc.new {
//your logic when to call the callback
}
或
after_save :after_save_callback, if: :call_if_condition
def call_if_condition
//condition for when to call the :after_save_callback method
end
call_if_condition
是一種方法。定義場景何時調用該方法中的after_save_callback
太棒了!我也在尋找內置的方法,但到目前爲止似乎沒有,但如果您可以設置一個特殊屬性來告訴Rails暫時暫停回調,那將是非常棒的......您的方法就像這樣,非常感謝! – Ivan 2008-10-19 11:37:10