Allow Users to Remove Uploaded Images with Paperclip

Allowing users to upload images is pretty straightforward in Rails – especially if you’re using the excellent Paperclip gem. I’ve blogged before about how easy it is to configure Paperclip to upload images to S3. However, there are no nice examples (that I could find) of allowing your users to remove images that are already uploaded.

Let’s start with a user model which has an attachment and a view where the user can upload an avatar photo.

class User < ActiveRecord::Base
  has_attached_file :avatar
end

If this view syntax looks rather strange (and awesome), take a look at Slim.

p
  = f.label :avatar
  = f.file_field :avatar

p
  - if @user.avatar?
    = image_tag @user.avatar.url(:thumb)

This will give us a form where a user can select a file to upload and view their current avatar, if they have one. Now we want to allow a user to remove their avatar from the site.

Let’s start off by adding a checkbox to the view for removing the avatar.

p
  = f.label :avatar
  = f.file_field :avatar

p
  - if @user.avatar?
    = image_tag @user.avatar.url(:thumb)
    = f.label :remove_avatar
      = f.check_box :remove_avatar
      | Remove avatar

Now I’m going to add a field to the User model which doesn’t map to a database field.

class User < ActiveRecord::Base
  attr_accessor :remove_avatar
  attr_accessible :remove_avatar
end

We now have non-persistent field in our model which will be populated when the user selects to remove his/her avatar. Now we simply need to remove the avatar when this flag is set.

class User < ActiveRecord::Base
  before_save :delete_avatar, if: ->{ remove_avatar == '1' && !avatar_updated_at_changed? }
  
  private

  def delete_avatar
    self.avatar = nil
  end
end

We need to do the check to see ensure that the avatar hasn’t changed because the user might check ‘remove avatar’ and still upload a new image. In this scenario I will assume the user is confused and simply upload their new avatar.

That’s all there is to it! I don’t really like the approach of using a before_save callback - a better approach would probably be to implement a service layer for updating the user and performing the logic there. But for a quick-and-easy approach this definitely works.

Happy coding.