Как обновить атрибуты в after_save, не вызывая рекурсии в rails 2.3?
У меня есть модель, у которой есть видео, прикрепленное с помощью 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 вызывает сохранение, которое затем вызывает обратный вызов before_save, вызывающий бесконечный цикл. Есть ли простой способ обойти это поведение?
Ответы
Ответ 1
Вы можете обернуть его в условное выражение, например:
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
Таким образом, он будет запускаться только один раз.
Ответ 2
Рельсы 2:
Model.send(:create_without_callbacks)
Model.send(:update_without_callbacks)
Рельсы 3:
Vote.skip_callback(:save, :after, :add_points_to_user)
Смотрите этот вопрос:
Как пропустить обратные вызовы ActiveRecord?
Ответ 3
Любой after_save
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
Ответ 4
Вы можете (и должны) проверить, действительно ли вам нужно обновить миниатюру:
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. Вы можете использовать переменную-член, чтобы указать изменения в случае, если она не может.
Ответ 5
Вместо этого вы можете запустить его как before_save
.
После того, как он был проверен, обновите миниатюру, затем отпустите ее, чтобы ее сохранить, но просто используйте методы присваивания
before_save :save_thumbnail
def save_thumbnail
self.thumbnail_file_name = File.basename(thumb),
self.thumbnail_content_type = 'image/jpeg'
end
Так как это не вызовет save, вы не будете рекурсивно, но он будет немедленно сохранен после выхода метода.
Что-то вроде этого должно работать, если только нет явной причины, по которой вам это нужно после сохранения.
Поскольку вы не обновляете отдельный объект, но тот же, это также сохранит вам вызов базы данных. Это как я делаю timestamps и тому подобное.