15

我有一个模型,其中附有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 回调导致无限循环。有没有一种简单的方法来规避这种行为?

4

5 回答 5

12

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

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

于 2014-02-28T10:22:27.707 回答
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

这样它只会运行一次。

于 2011-07-13T15:23:18.540 回答
8

导轨 2:

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

导轨 3:

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

看到这个问题:

如何跳过 ActiveRecord 回调?

于 2011-07-13T15:30:34.653 回答
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 中的属性变化。您可以使用成员变量来指示更改,以防万一。

于 2011-07-13T15:41:47.293 回答
0

您可以before_save改为运行它。

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

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

由于这不会调用保存,因此您不会递归,但它会在方法退出后立即保存。

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

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

于 2011-07-13T15:26:32.267 回答