Rails Paperclip how to delete attachment?
Asked Answered
B

6

86

I am using Paperclip (w/ Amazon s3) on Rails 3. I want to delete an existing attachment without replacing it using an update action.

I've only found one example of this here and could not get that to work, it just wouldn't delete and there was nothing in the logs to say why. I wanted to do something like this on the form:

<%- unless @page.new_record? || [email protected]? -%>
    <%= f.check_box :image_delete, :label => 'Delete Image' %>
<%- end -%>

(page is the name of the model, image is the attribute name which holds the attachment)

But how do I detect that checkbox and more importantly, how do I delete the image? I appreciate any help!

Braiding answered 14/12, 2010 at 4:19 Comment(0)
S
107

First off, when you create a check_box in a form_for (which it looks like you are), then the form should by default send :image_delete as "1" if checked and "0" if unchecked. The method declaration looks like this:

def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")

Which shows that you can assign other values if you want to, but that is of course optional.

Secondly, the call to manually delete an attachment without deleting the model instance to which it is attached to is:

@page.image.destroy #Will remove the attachment and save the model
@page.image.clear #Will queue the attachment to be deleted

And to accomplish your way of deleting the images through a checkbox, perhaps add something like this to your Page model:

class Page < ActiveRecord::Base
  has_attached_file :image

  before_save :destroy_image?

  def image_delete
    @image_delete ||= "0"
  end

  def image_delete=(value)
    @image_delete = value
  end

private
  def destroy_image?
    self.image.clear if @image_delete == "1"
  end
end

This way, when you create your form and add the :image_delete checkbox, it will load the default value "0" from the User instance. And if that field is checked then the controller will update the image_delete to "1" and when the User is saved, it will check if the image is to be deleted.

Speculum answered 14/12, 2010 at 5:12 Comment(6)
In this example, does Page#image refer to another model which has_attached_file, or does Page have the attachment, named image?Jarrad
@page is the model variable that has_attached_file :image, but I seem to have named the model User for some reason. I'll change and update to clarify.Speculum
Okay, that makes more sense :)Jarrad
I don't understand why you don't just do self.image.destroy there -- does clear remove the underlying file, but maintain the meta information about image in the Page model? Why would you want to do this? (and it doesn't seem like that's what the question asker wants to do)Jarrad
This approach also worked for me... but I faced one issue... if the user checks image_delete checkbox and also add a new image at the same time in the form, then the old image is deleted and the new image is not saved. I solved this by changing the condition to self.image.clear if @image_delete == "1" and !image.dirty? in destroy_image? methodRaneeraney
As I experienced @page.image.destroy only delete attachment in the cloud. So you specifically need to save your model record @page.save. Otherwise your record will be considered still having an attached image/file but the generated link would point to an error. (Rails 4.1 / Paperclip 4.2.0 / Amazon S3)Trifocals
P
98

has_attached_file :asset

=>

    attr_accessor :delete_asset
    before_validation { asset.clear if delete_asset == '1' }

No need to destroy asset, Paperclip will do it.

In the form form.check_box(:delete_asset) will suffice.

Participation answered 9/11, 2011 at 8:40 Comment(6)
This works and it's simpler than the @Speculum answer IMHO. Very good! :)Jeopardize
How would you write a spec for this?Wallflower
Thanks ! To help me slim this down even more to : has_attached_file :asset has_destroyable_file :asset I created an initializer to add to config/initializers/ gist.github.com/3954054Martsen
I found a problem with this method through accepts_nested_attributes at least. before_validation doesn't get triggered on a nested save if no other attributes have been altered. See my answer below for the solutionIvories
I keep getting this error in Rails 4. Unpermitted parameters: delete_assetAnarthrous
@SurgePedroza I believe you need to permit the parameter :delete_asset, see guides.rubyonrails.org/…Mariandi
I
12

This is Benoit's answer, but wrapped in a module, and covering the edge case of nested attribute models where the destroy tickbox is the only thing changed on the model.

It will apply to all attachments on the model.

# This needs to be included after all has_attached_file statements in a class
module DeletableAttachment
  extend ActiveSupport::Concern

  included do
    attachment_definitions.keys.each do |name|

      attr_accessor :"delete_#{name}"

      before_validation { send(name).clear if send("delete_#{name}") == '1' }

      define_method :"delete_#{name}=" do |value|
        instance_variable_set :"@delete_#{name}", value
        send("#{name}_file_name_will_change!")
      end

    end
  end

end
Ivories answered 28/5, 2014 at 10:2 Comment(9)
Don't know why this hasn't gotten more attention. attachment_definitions just saved my life.Metallurgy
Needs this line too though: attr_accessible :"delete_#{name}"Metallurgy
So do I put this in the same file as the model? Where in it exactly?Tracey
The example above should be in your concerns or model folder. In the model where you want it just add the line include DeletableAttachment below any has_attached_file statementsIvories
In rails3 you will need attr_accessible :"delete_#{name}" alsoCryptograph
@PaulOdeon What does the define method in your code do an why do I need a file name in the last line? If I'm deleting an image, isn't that happening in the before validation line?Pretend
@emm could you respond to my comment above? I pasted the module code below my has_attached block in the model. then added a checkbox in form. not sure what i'm missing.Pretend
name is the name of of the file you have defined i.e. "photo" you will therefore need to have f.checkbox :delete_photo define-method dynamically creates the function that will mark the file for deletionIvories
Remember to permit :delete_<your_attribute> if you are using strong parameters in your controllerInvolved
C
5

remember to add this to your Page model too:

attr_accessible :image_delete
Challenge answered 11/8, 2011 at 14:48 Comment(0)
A
1

Modified version of Paul's solution, to support Rails 5 custom attributes. I just wish there were a way to include the module at the top of the file, before has_attached_file definitions.

module Mixins
  module PaperclipRemover

    extend ActiveSupport::Concern

    included do
      attachment_definitions.keys.each do |name|

        attribute :"remove_#{name}", :boolean

        before_validation do
          self.send("#{name}=", nil) if send("remove_#{name}?")
        end

      end
    end

  end

end
Arian answered 25/1, 2018 at 18:31 Comment(0)
P
0

Was able to achieve this with less code, by just implementing a delete_attachment on the model's side.:

class MyModel < ApplicationRecord
  has_attached_file :image

  def image_delete=(other)
    self.image = nil if other == "1" or other == true
  end
end
Pulmonary answered 7/5, 2020 at 13:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.