TL;DR Use user.update!(remove_avatar: true)
to delete attachments outside of forms. This will have the same behavior as if you were in a form.
As you know, Carrierwave file attachments work by mounting an Uploader
class to an attribute of the model. Though the database field holds the file name as string, calling the attribute will always return the uploader, no matter if a file is attached or not. (Side note: use #present?
on the uploader to check if the file exists.)
class User < ApplicationRecord
mount :avatar, AvatarUploader
end
Now there are several ways to delete an attached file. The invocations do not have the same effect (while each is correct in its domain):
- Setting
remove_avatar
totrue
and saving:This lives in the ActiveRecord domain. It will delete the file AND empty the database field.user.remove_avatar = true user.save! # or even shorter user.update!(remove_avatar: true)
Advantages:- The behavior is the same as in a usual form. First the DB field gets cleared and the transaction commits, then the file gets deleted in a callback.
- Using the dynamic
#remove_avatar!
Show archive.org snapshot method Carrierwave patches onto the modelThe methoduser.remove_avatar! user.save!
#remove_avatar!
on the model lives in the ActiveRecord domain. It will delete the file AND empty the database field. Subsequent calls toavatar.present?
will returnfalse
.
Note that Carrierwave itself will NOT touch the database, so you need to save the model after removing its attachment in order to persist its removal (the file is gone nonetheless, but without saving the model would forget).
Also note that an already instantiated record will never know that its file is gone if you delete the file from another instantiation. Example:u = User.last u.avatar.present? # => true User.find_each do |u| u.remove_avatar! u.save! end u.avatar.present? # => true u.reload.avatar.present? # => true User.find(u.id).avatar.present? # => false
Note
#remove_avatar!
itself does not save. Carrierwave internally calls this method as andafter_commit
callback, so it saves to the DB first and removes the file afterwards. If you call this method manually and then save, you risk that your change to the DB will fail after you already deleted the file. - Using the
#remove!
Show archive.org snapshot method from the uploader:NOTE:user.avatar.remove!
#remove!
on the uploader lives in the "file storage" domain. It will delete the file, but leave the database field untouched, so the uploader will believe it is still there. Subsequent calls toavatar.present?
will returntrue
.
Which one should you use?
Unless you deal with low-level logic and have good reasons to do otherwise, probably stick with option 1.