Rails Paperclip ¿cómo eliminar un archivo adjunto?

84

Estoy usando Paperclip (con Amazon s3) en Rails 3. Quiero eliminar un archivo adjunto existente sin reemplazarlo usando una acción de actualización.

Solo encontré un ejemplo de esto aquí y no pude hacer que funcionara, simplemente no se borraba y no había nada en los registros para decir por qué. Quería hacer algo como esto en el formulario:

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

(la página es el nombre del modelo, la imagen es el nombre del atributo que contiene el archivo adjunto)

Pero, ¿cómo detecto esa casilla de verificación y, lo que es más importante, cómo elimino la imagen? ¡Agradezco cualquier ayuda!

jyoseph
fuente

Respuestas:

104

En primer lugar, cuando crea una casilla de verificación en un formulario_para (que parece que es usted), entonces el formulario debe enviar por defecto: image_delete como "1" si está marcado y "0" si no está marcado. La declaración del método se ve así:

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

Lo que muestra que puede asignar otros valores si lo desea, pero eso, por supuesto, es opcional.

En segundo lugar, la llamada para eliminar manualmente un archivo adjunto sin eliminar la instancia del modelo a la que está adjunto es:

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

Y para lograr su forma de eliminar las imágenes a través de una casilla de verificación, tal vez agregue algo como esto a su modelo de página:

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

De esta manera, cuando crea su formulario y agrega la casilla de verificación: image_delete, se cargará el valor predeterminado "0" de la instancia de Usuario. Y si ese campo está marcado, el controlador actualizará image_delete a "1" y cuando se guarde el usuario, verificará si la imagen debe eliminarse.

DanneManne
fuente
En este ejemplo, ¿la imagen de número de página se refiere a otro modelo que has_attached_file, o la página tiene el archivo adjunto, imagen con nombre?
John Bachir
@page es la variable modelo que has_attached_file: image, pero parece que he nombrado al usuario modelo por alguna razón. Cambiaré y actualizaré para aclarar.
DanneManne
Está bien, eso tiene más sentido :)
John Bachir
No entiendo por qué no solo hace self.image.destroy allí, ¿borra el archivo subyacente, pero mantiene la metainformación sobre la imagen en el modelo de página? Por qué querrías hacer esto? (y no parece que eso sea lo que el autor de la pregunta quiere hacer)
John Bachir
11
Este enfoque también funcionó para mí ... pero enfrenté un problema ... si el usuario marca la casilla de verificación image_delete y también agrega una nueva imagen al mismo tiempo en el formulario, entonces la imagen anterior se elimina y la nueva imagen no se guarda . Resolví esto cambiando la condición al método self.image.clear if @image_delete == "1" and !image.dirty?indestroy_image?
Zeeshan
97

has_attached_file :asset

=>

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

No es necesario destruir el activo, Paperclip lo hará.

En la forma form.check_box(:delete_asset)será suficiente.

Benoit B.
fuente
3
Esto funciona y es más simple que la respuesta de @DanneManne en mi humilde opinión. ¡Muy bien! :)
MetalElf0
¿Cómo escribirías una especificación para esto?
Hengjie
1
Gracias ! Para ayudarme a reducir esto aún más: has_attached_file :asset has_destroyable_file :asset creé un inicializador para agregar a config/initializers/ gist.github.com/3954054
soleado
2
Encontré un problema con este método a través de accept_nested_attributes al menos. before_validation no se activa en un guardado anidado si no se han modificado otros atributos. Vea mi respuesta a continuación para la solución
Paul Odeon
4
@SurgePedroza Creo que debe permitir el parámetro: delete_asset, consulte guides.rubyonrails.org/…
sman591
12

Esta es la respuesta de Benoit, pero envuelta en un módulo y que cubre el caso de borde de los modelos de atributos anidados donde la casilla de verificación de destrucción es lo único que ha cambiado en el modelo.

Se aplicará a todos los archivos adjuntos del modelo.

# 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
Paul Odeon
fuente
1
No sé por qué esto no ha recibido más atención. attachment_definitionsme acaba de salvar la vida.
okay56k
Sin embargo, también necesita esta línea:attr_accessible :"delete_#{name}"
okay56k
2
El ejemplo anterior debe estar en su carpeta de inquietudes o modelo. En el modelo donde lo desee, simplemente agregue la línea include DeletableAttachmentdebajo de las has_attached_filedeclaraciones
Paul Odeon
2
En rails3 necesitarás attr_accessible: "delete _ # {name}" también
Mateu
1
Recuerde permitir :delete_<your_attribute>si está utilizando parámetros fuertes en su controlador
ivanxuu
5

recuerde agregar esto también a su modelo de página:

attr_accessible :image_delete
Glenn McW
fuente
1

Versión modificada de la solución de Paul, para admitir atributos personalizados de Rails 5. Solo desearía que hubiera una forma de incluir el módulo en la parte superior del archivo, antes de las has_attached_filedefiniciones.

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
JBlake
fuente
0

Pude lograr esto con menos código, simplemente implementando un delete_attachmenten el lado del modelo:

class MyModel < ApplicationRecord
  has_attached_file :image

  def image_delete=(other)
    self.image = nil if other == "1" or other == true
  end
end
stwienert
fuente