Rails Paperclip come eliminare l'allegato?


84

Utilizzo Paperclip (con Amazon s3) su Rails 3. Desidero eliminare un allegato esistente senza sostituirlo utilizzando un'azione di aggiornamento.

Ho trovato solo un esempio di questo qui e non sono riuscito a farlo funzionare, semplicemente non sarebbe stato eliminato e non c'era nulla nei registri per dire perché. Volevo fare qualcosa del genere sul modulo:

<%- unless @page.new_record? || !@page.image? -%>
    <%= f.check_box :image_delete, :label => 'Delete Image' %>
<%- end -%>

(la pagina è il nome del modello, l'immagine è il nome dell'attributo che contiene l'allegato)

Ma come faccio a rilevare quella casella di controllo e, cosa più importante, come elimino l'immagine? Apprezzo qualsiasi aiuto!

Risposte:


104

Prima di tutto, quando crei una casella di controllo in un form_for (che sembra che tu sia), il modulo dovrebbe inviare per impostazione predefinita: image_delete come "1" se selezionato e "0" se non selezionato. La dichiarazione del metodo ha questo aspetto:

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

Il che mostra che puoi assegnare altri valori se lo desideri, ma questo è ovviamente opzionale.

In secondo luogo, la chiamata per eliminare manualmente un allegato senza eliminare l'istanza del modello a cui è collegato è:

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

E per ottenere il tuo modo di eliminare le immagini tramite una casella di controllo, forse aggiungi qualcosa del genere al tuo modello di pagina:

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

In questo modo, quando crei il tuo modulo e aggiungi la casella di controllo: image_delete, caricherà il valore predefinito "0" dall'istanza dell'utente. E se quel campo è selezionato, il controller aggiornerà image_delete a "1" e quando l'utente viene salvato, controllerà se l'immagine deve essere eliminata.


In questo esempio, Page # image si riferisce a un altro modello che has_attached_file, o Page ha l'allegato, denominato image?
John Bachir

@page è la variabile del modello che has_attached_file: image, ma sembra che per qualche motivo abbia chiamato l'utente del modello. Modificherò e aggiornerò per chiarire.
DanneManne

Ok, ha più senso :)
John Bachir

Non capisco perché non fai solo self.image.destroy lì - rimuove chiaramente il file sottostante, ma mantieni le meta informazioni sull'immagine nel modello di pagina? Perché dovresti farlo? (e non sembra che sia quello che vuole fare il richiedente)
John Bachir

11
Questo approccio ha funzionato anche per me ... ma ho riscontrato un problema ... se l'utente seleziona la casella di controllo image_delete e aggiunge anche una nuova immagine allo stesso tempo nel modulo, la vecchia immagine viene eliminata e la nuova immagine non viene salvata . Ho risolto questo problema modificando la condizione self.image.clear if @image_delete == "1" and !image.dirty?in destroy_image?metodo
Zeeshan

97

has_attached_file :asset

=>

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

Non c'è bisogno di distruggere la risorsa, Paperclip lo farà.

Nella forma form.check_box(:delete_asset)sarà sufficiente.


3
Funziona ed è più semplice della risposta di @DanneManne IMHO. Molto buona! :)
MetalElf0

Come scriveresti una specifica per questo?
Hengjie

1
Grazie ! Per aiutarmi a snellire ancora di più: has_attached_file :asset has_destroyable_file :asset ho creato un inizializzatore da aggiungere a config/initializers/ gist.github.com/3954054
Sunny

2
Ho riscontrato un problema con questo metodo almeno tramite accept_nested_attributes. before_validation non viene attivato su un salvataggio annidato se nessun altro attributo è stato alterato. Vedi la mia risposta sotto per la soluzione
Paul Odeon

4
@SurgePedroza Credo che tu debba consentire il parametro: delete_asset, vedi guides.rubyonrails.org/…
sman591

12

Questa è la risposta di Benoit, ma racchiusa in un modulo e copre il caso limite dei modelli di attributi annidati in cui la casella di controllo distruggi è l'unica cosa modificata sul modello.

Si applicherà a tutti gli allegati sul modello.

# 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
Non so perché questo non abbia ricevuto più attenzione. attachment_definitionsmi ha appena salvato la vita.
ok 56k

Ha bisogno anche di questa linea però:attr_accessible :"delete_#{name}"
okay56k

2
L'esempio sopra dovrebbe essere nelle tue preoccupazioni o nella cartella del modello. Nel modello in cui lo desideri, aggiungi la riga include DeletableAttachmentsotto qualsiasi has_attached_filedichiarazione
Paul Odeon

2
In rails3 avrai bisogno anche di attr_accessible: "delete _ # {name}"
Mateu

1
Ricordati di consentire :delete_<your_attribute>se stai usando parametri forti nel tuo controller
ivanxuu

5

ricordati di aggiungere anche questo al tuo modello di pagina:

attr_accessible :image_delete

1

Versione modificata della soluzione di Paul, per supportare gli attributi personalizzati di Rails 5. Vorrei solo che ci fosse un modo per includere il modulo all'inizio del file, prima delle has_attached_filedefinizioni.

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

0

È stato in grado di raggiungere questo obiettivo con meno codice, implementando semplicemente un delete_attachmentsul lato del modello .:

class MyModel < ApplicationRecord
  has_attached_file :image

  def image_delete=(other)
    self.image = nil if other == "1" or other == true
  end
end
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.