如何在不触发activerecord回调的情况下添加activestorage附件

huangapple go评论53阅读模式
英文:

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 &lt; 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: &quot;ComplicatedModel_#{self.id}.json&quot;,
      content_type: &#39;application/json&#39;
    ) 
  end
end

huangapple
  • 本文由 发表于 2023年2月18日 00:24:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/75486841.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定