Rails Paperclip comment supprimer une pièce jointe?

84

J'utilise Paperclip (avec Amazon s3) sur Rails 3. Je souhaite supprimer une pièce jointe existante sans la remplacer à l' aide d'une action de mise à jour.

Je n'ai trouvé qu'un exemple de cela ici et je n'ai pas pu le faire fonctionner, cela ne serait tout simplement pas supprimé et il n'y avait rien dans les journaux pour dire pourquoi. Je voulais faire quelque chose comme ça sur le formulaire:

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

(page est le nom du modèle, image est le nom de l'attribut qui contient la pièce jointe)

Mais comment détecter cette case à cocher et, plus important encore, comment supprimer l'image? J'apprécie toute aide!

jyoseph
la source

Réponses:

104

Tout d'abord, lorsque vous créez une case à cocher dans un form_for (qui ressemble à vous), le formulaire doit par défaut envoyer: image_delete comme "1" si coché et "0" si décoché. La déclaration de méthode ressemble à ceci:

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

Ce qui montre que vous pouvez attribuer d'autres valeurs si vous le souhaitez, mais cela est bien sûr facultatif.

Deuxièmement, l'appel pour supprimer manuellement une pièce jointe sans supprimer l'instance de modèle à laquelle elle est attachée est:

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

Et pour accomplir votre façon de supprimer les images via une case à cocher, ajoutez peut-être quelque chose comme ceci à votre modèle de page:

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 cette façon, lorsque vous créez votre formulaire et ajoutez la case à cocher: image_delete, il chargera la valeur par défaut "0" à partir de l'instance User. Et si ce champ est coché, le contrôleur mettra à jour l'image_delete à "1" et lorsque l'utilisateur sera enregistré, il vérifiera si l'image doit être supprimée.

DanneManne
la source
Dans cet exemple, l'image n ° de page fait-elle référence à un autre modèle has_attached_file, ou la page a-t-elle la pièce jointe, nommée image?
John Bachir
@page est la variable de modèle qui has_attached_file: image, mais il me semble avoir nommé le modèle User pour une raison quelconque. Je vais changer et mettre à jour pour clarifier.
DanneManne
D'accord, cela a plus de sens :)
John Bachir
Je ne comprends pas pourquoi vous ne faites pas simplement self.image.destroy là-bas - est-ce que clear supprime le fichier sous-jacent, mais conserve les méta-informations sur l'image dans le modèle de page? Pourquoi voudriez-vous faire ça? (et il ne semble pas que ce soit ce que le poseur de question veut faire)
John Bachir
11
Cette approche a également fonctionné pour moi ... mais j'ai rencontré un problème ... si l'utilisateur coche la case image_delete et ajoute également une nouvelle image en même temps dans le formulaire, l'ancienne image est supprimée et la nouvelle image n'est pas enregistrée . J'ai résolu cela en changeant la condition self.image.clear if @image_delete == "1" and !image.dirty?en destroy_image?méthode
Zeeshan
97

has_attached_file :asset

=>

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

Pas besoin de détruire un actif, Paperclip le fera.

Dans la forme form.check_box(:delete_asset)suffira.

Benoit B.
la source
3
Cela fonctionne et c'est plus simple que la réponse @DanneManne à mon humble avis. Très bien! :)
MetalElf0
Comment écririez-vous une spécification pour cela?
Hengjie
1
Merci ! Pour m'aider à réduire encore plus cela: has_attached_file :asset has_destroyable_file :asset j'ai créé un initialiseur à ajouter à config/initializers/ gist.github.com/3954054
Sunny
2
J'ai trouvé un problème avec cette méthode via au moins accepte_nested_attributes. before_validation ne se déclenche pas sur une sauvegarde imbriquée si aucun autre attribut n'a été modifié. Voir ma réponse ci-dessous pour la solution
Paul Odeon
4
@SurgePedroza Je crois que vous devez autoriser le paramètre: delete_asset, voir guides.rubyonrails.org
...
12

C'est la réponse de Benoit, mais enveloppée dans un module, et couvrant le cas de bord des modèles d'attributs imbriqués où la case à cocher détruire est la seule chose modifiée sur le modèle.

Il s'appliquera à toutes les pièces jointes du modèle.

# 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 Odéon
la source
1
Je ne sais pas pourquoi cela n'a pas attiré plus d'attention. attachment_definitionsvient de me sauver la vie.
ok56k
A besoin de cette ligne aussi:attr_accessible :"delete_#{name}"
okay56k
2
L'exemple ci-dessus doit être dans vos préoccupations ou dans le dossier du modèle. Dans le modèle où vous le souhaitez, ajoutez simplement la ligne include DeletableAttachmentsous toutes les has_attached_filedéclarations
Paul Odeon
2
Dans rails3, vous aurez besoin de attr_accessible: "delete _ # {name}" aussi
Mateu
1
N'oubliez pas de permettre :delete_<your_attribute>si vous utilisez des paramètres forts dans votre contrôleur
ivanxuu
5

n'oubliez pas de l'ajouter également à votre modèle de page:

attr_accessible :image_delete
Glenn McW
la source
1

Version modifiée de la solution de Paul, pour prendre en charge les attributs personnalisés de Rails 5. Je souhaite juste qu'il y ait un moyen d'inclure le module en haut du fichier, avant les has_attached_filedéfinitions.

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
la source
0

A pu y parvenir avec moins de code, en implémentant simplement un delete_attachmentcôté modèle:

class MyModel < ApplicationRecord
  has_attached_file :image

  def image_delete=(other)
    self.image = nil if other == "1" or other == true
  end
end
Stwienert
la source