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

时间:2011-07-13 15:20:43

标签: ruby-on-rails callback save ruby-on-rails-2

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

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

我最好使用像这样的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回调导致无限循环。是否有一种简单的方法来规避这种行为?

5 个答案:

答案 0 :(得分:9)

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

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

这样它只能运行一次。

答案 1 :(得分:8)

Rails 2:

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

Rails 3:

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

看到这个问题:

How to skip ActiveRecord callbacks?

答案 2 :(得分:8)

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

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

答案 3 :(得分:2)

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

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中看到属性更改。您可以使用成员变量来指示更改,如果不能。

答案 4 :(得分:0)

您可以将其作为before_save运行。

验证完毕后,更新缩略图,然后继续保存,但只需使用分配方法

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

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

这样的事情应该有效,除非在保存之后有明确的理由需要它。

由于您没有更新单独的对象,而是同一个对象,因此这也将为您节省数据库调用。这就是我如何做时间戳和类似的东西。