86 votes

Rails Paperclip : comment supprimer une pièce jointe ?

J'utilise Paperclip (avec Amazon s3) sur Rails 3. Je veux supprimer une pièce jointe existante sans le remplacer en utilisant une action de mise à jour.

Je n'ai trouvé qu'un seul exemple de cela aquí et je n'ai pas réussi à le faire fonctionner, il n'a tout simplement pas été supprimé et il n'y avait rien dans les journaux pour dire pourquoi. Je voulais faire quelque chose comme ceci sur le formulaire :

<%- unless @page.new_record? || !@page.image? -%>
    <%= 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 surtout, comment supprimer l'image ? J'apprécie toute aide !

107voto

DanneManne Points 13408

Tout d'abord, lorsque vous créez une case à cocher dans un form_for (ce qui semble être le cas), le formulaire doit par défaut envoyer :image_delete sous la forme "1" si elle est cochée et "0" si elle n'est pas cochée. La déclaration de la 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 permettant de supprimer manuellement une pièce jointe sans supprimer l'instance de modèle à laquelle elle est attachée :

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

Et pour réaliser votre méthode de suppression des images par le biais d'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

Ainsi, lorsque vous créez votre formulaire et que vous ajoutez la case à cocher :image_delete, il chargera la valeur par défaut "0" 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.

0 votes

Dans cet exemple, est-ce que Page#image fait référence à un autre modèle qui has_attached_file ou est-ce que Page a la pièce jointe, nommée image ?

0 votes

@page est la variable modèle qui has_attached_file :image, mais il semble que j'ai nommé le modèle User pour une raison quelconque. Je vais changer et mettre à jour pour clarifier.

0 votes

Ok, c'est plus logique :)

98voto

Benoit B. Points 1009

has_attached_file :asset

\=>

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

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

Dans le formulaire form.check_box(:delete_asset) suffira.

3 votes

Cela fonctionne et c'est plus simple que la réponse de @DanneManne IMHO. Très bien ! :)

0 votes

Comment rédiger une spécification pour cela ?

1 votes

Merci ! Pour m'aider à réduire encore plus la taille à : has_attached_file :asset has_destroyable_file :asset J'ai créé un initialisateur à ajouter à config/initializers/ gist.github.com/3954054

12voto

Paul Odeon Points 534

C'est la réponse de Benoît, mais enveloppée dans un module, et couvrant le cas limite des modèles d'attributs imbriqués où la case à cocher de destruction 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

1 votes

Je ne sais pas pourquoi ça n'a pas attiré plus d'attention. attachment_definitions vient de me sauver la vie.

0 votes

Il a besoin de cette ligne aussi : attr_accessible :"delete_#{name}"

0 votes

Dois-je le mettre dans le même fichier que le modèle ? Où dans ce fichier exactement ?

5voto

Glenn McW Points 74

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

attr_accessible :image_delete

1voto

JBlake Points 143

Version modifiée de la solution de Paul, pour supporter les attributs personnalisés de Rails 5. J'aimerais juste qu'il y ait un moyen d'inclure le module en haut du fichier, avant que has_attached_file dé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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X