如何更新 after_save 中的属性而不导致 Rails 2.3 中的递归?

发布于 2024-11-24 07:20:52 字数 591 浏览 2 评论 0原文

我有一个模型,其中附有 Paperclip 的视频。保存后,我使用保存的视频生成缩略图。我需要在每次保存后执行此操作,即使尚未上传新视频,因为用户可以更改捕获缩略图的时间。

我目前正在使用 after_post_process 来执行此操作,但它只会在上传文件时生成缩略图(这是回形针的一部分的回调)。

理想情况下,我会使用像这样的 after_save 回调:

after_save :save_thumbnail
def save_thumbnail
  #generate thumbnail...
  self.update_attributes(
    :thumbnail_file_name => File.basename(thumb), 
    :thumbnail_content_type => 'image/jpeg'
  )
end

不幸的是 update_attributes 调用 save,然后调用 before_save 回调导致无限循环。有没有一种简单的方法来规避这种行为?

I've got a model which has a video attached with Paperclip. After it saves I use the saved video to generate a thumbnail. I need to do this after every save, even when a new video hasn't been uploaded, because the user can change the time where the thumbnail is captured.

I am currently using after_post_process to do this, but it will only generate the thumbnail when uploading a file (this is a callback which is part of Paperclip).

I would ideally use an after_save callback like this:

after_save :save_thumbnail
def save_thumbnail
  #generate thumbnail...
  self.update_attributes(
    :thumbnail_file_name => File.basename(thumb), 
    :thumbnail_content_type => 'image/jpeg'
  )
end

Unfortunately update_attributes calls save, which then calls the before_save callback causing an infinite loop. Is there a simple way to circumvent this behaviour?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

野の 2024-12-01 07:20:52

在 Rails3+ 中,after_save 回调中的任何 update_attribute 都会导致递归。
应该做的是:

after_save :updater!
# Awesome Ruby code
# ...
# ...

private

  def updater!
    self.update_column(:column_name, new_value) # This will skip validation gracefully.
  end

这里有一些关于它的文档: https://guides.rubyonrails .org/active_record_callbacks.html#skipping-callbacks

Any update_attribute in an after_save callback will cause recursion, in Rails3+.
What should be done is:

after_save :updater!
# Awesome Ruby code
# ...
# ...

private

  def updater!
    self.update_column(:column_name, new_value) # This will skip validation gracefully.
  end

Here is some documentation about it: https://guides.rubyonrails.org/active_record_callbacks.html#skipping-callbacks

撕心裂肺的伤痛 2024-12-01 07:20:52

您可以将其包装在条件中,例如:

def save_thumbnail
  if File.basename(thumb) != thumbnail_file_name
    self.update_attributes(
      :thumbnail_file_name => File.basename(thumb), 
      :thumbnail_content_type => 'image/jpeg'
    )
  end
end

这样它只会运行一次。

You could wrap it in a conditional, something like:

def save_thumbnail
  if File.basename(thumb) != thumbnail_file_name
    self.update_attributes(
      :thumbnail_file_name => File.basename(thumb), 
      :thumbnail_content_type => 'image/jpeg'
    )
  end
end

That way it would only run once.

心碎无痕… 2024-12-01 07:20:52

Rails 2:

Model.send(:create_without_callbacks)
Model.send(:update_without_callbacks)

Rails 3:

Vote.skip_callback(:save, :after, :add_points_to_user)

请参阅此问题:

如何跳过 ActiveRecord 回调?

Rails 2:

Model.send(:create_without_callbacks)
Model.send(:update_without_callbacks)

Rails 3:

Vote.skip_callback(:save, :after, :add_points_to_user)

See this question:

How to skip ActiveRecord callbacks?

提笔书几行 2024-12-01 07:20:52

您可以(并且应该)检查是否确实需要更新缩略图:

after_save :save_thumbnail
def save_thumbnail
  if capture_time_changed? #assuming capture_time contains time when the thumbnail has to be captured
    #generate thumbnail...
    self.update_attributes(
      :thumbnail_file_name => File.basename(thumb), 
      :thumbnail_content_type => 'image/jpeg'
    )
  end
end

在这里您可以阅读有关“脏”属性的更多信息:http://apidock.com/rails/ActiveRecord/Dirty

虽然我不确定它是否仍然可以看到after_save中的属性变化。您可以使用成员变量来指示更改,以防万一它不能。

You can(and should) check if you actually need to update the thumbnail:

after_save :save_thumbnail
def save_thumbnail
  if capture_time_changed? #assuming capture_time contains time when the thumbnail has to be captured
    #generate thumbnail...
    self.update_attributes(
      :thumbnail_file_name => File.basename(thumb), 
      :thumbnail_content_type => 'image/jpeg'
    )
  end
end

Here you can read more about 'dirty' attributes: http://apidock.com/rails/ActiveRecord/Dirty

Although I'm not sure if it still can see the attribute changes in after_save. You can use a member variable to indicate changes in case it can't.

半寸时光 2024-12-01 07:20:52

您可以将其作为 before_save 运行。

验证通过后,更新缩略图,然后让它继续保存,但只需使用赋值方法即可,

before_save :save_thumbnail
def save_thumbnail
  self.thumbnail_file_name = File.basename(thumb), 
  self.thumbnail_content_type = 'image/jpeg'
end

因为不会调用save,所以不会递归,但方法退出后会立即保存。

类似的东西应该可以工作,除非有明确的原因您在保存后需要它。

由于您不是更新单独的对象,而是更新同一个对象,因此这也将为您节省一次数据库调用。这也是我做时间戳和类似事情的方式。

You can run it as a before_save instead.

After it has been validated, update the thumbnail, then let it go on to be saved, but just use the assignment methods

before_save :save_thumbnail
def save_thumbnail
  self.thumbnail_file_name = File.basename(thumb), 
  self.thumbnail_content_type = 'image/jpeg'
end

Since that won't call save, you wont recurse, but it will immediately be saved after the method exits.

Something like that should work, unless there is an explicit reason you need it in after save.

Since you are not updating a separate object, but the same one, this will save you a database call as well. This is How i do timestamps and things like that too.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文