英文:
How to add an activestorage attachment without triggering activerecord callbacks
问题
问题在于在 aftersave 回调中更新 activestorage 附件会创建一个无限循环:这一步再次保存模型,因此重新触发回调。为了使这个策略奏效,我需要在不触发 aftersave 回调的情况下更新 activestorage 附件。这可行吗?
英文:
In my app, I have a large complex model with many associations. I need to serve it as json. The model's size and complexity, however, mean that assembling the json version is costly both in terms of CPU and RAM. Fortunately, since the model is not frequently updated it's a good candidate for caching. I'd like to keep a cached version of the model's json string on a static file hosting service (e.g. S3), handled by activestorage.
The code below summarizes what's I've done so far. In the controller, incoming GET requests for the model are redirected to the cached version on activestorage. The model keeps the cached version up-to-date using an after_save
callback.
class ComplicatedModelController < ApplicationController
def show
@complicated_model = ComplicatedModel.find(params[:id])
redirect_to @complicated_model.activestorage_cached_json_response.url
end
class ComplicatedModel < ActiveRecord::Base
has_one_attached :activestorage_cached_json_response
after_save do
# updated cached version - how can I do this WITHOUT retriggering the aftersave callback?
self.activestorage_cached_json_response.attach(
io: StringIO.new(self.as_json), # this step is costly
filename: "ComplicatedModel_#{self.id}.json",
content_type: 'application/json'
)
end
end
The problem is that updating the activestorage attachment within the aftersave callback creates an infinite loop: this step saves the model again, and so retriggers the callback. To make this strategy work, I'd need to update the activestorage attachment WITHOUT triggering the aftersave callback. Is this possible?
答案1
得分: 0
I just needed to use an :unless after the callback, as is explained here: https://stackoverflow.com/a/7386222/765287
Working code is below.
class ComplicatedModel < ActiveRecord::Base
attr_accessor :skip_aftersave_callback
has_one_attached :activestorage_cached_json_response
after_save :update_cache, unless: :skip_aftersave_callback # change here
def update_cache
# update_cache is always called in the after_save callback;
# this flag stops the callback after the .attach below
self.skip_aftersave_callback = true
self.activestorage_cached_json_response.attach(
io: StringIO.new(self.as_json), # this step is costly
filename: "ComplicatedModel_#{self.id}.json",
content_type: 'application/json'
)
end
end
(Note: The code is provided as it is, without translation.)
英文:
Ah, I figured it out. I just needed to use an :unless after the callback, as is explained here: https://stackoverflow.com/a/7386222/765287
Working code is below.
class ComplicatedModel < ActiveRecord::Base
attr_accessor :skip_aftersave_callback
has_one_attached :activestorage_cached_json_response
after_save :update_cache, unless: :skip_aftersave_callback # change here
def update_cache
# update_cache is always called in the after_save callback;
# this flag stops the callback after the .attach below
self.skip_aftersave_callback = true
self.activestorage_cached_json_response.attach(
io: StringIO.new(self.as_json), # this step is costly
filename: "ComplicatedModel_#{self.id}.json",
content_type: 'application/json'
)
end
end
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论