我有一个附有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回调导致无限循环。是否有一种简单的方法来规避这种行为?
答案 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)
看到这个问题:
答案 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,所以你不会递归,但是在方法退出后会立即保存。
这样的事情应该有效,除非在保存之后有明确的理由需要它。
由于您没有更新单独的对象,而是同一个对象,因此这也将为您节省数据库调用。这就是我如何做时间戳和类似的东西。