Rails Paperclip jak usunąć załącznik?

84

Używam Paperclip (w / Amazon s3) na Railsach 3. Chcę usunąć istniejący załącznik bez zastępowania go za pomocą akcji aktualizacji.

Znalazłem tylko jeden przykład to tutaj i że nie może dostać się do pracy, to po prostu nie usuwać i nie było nic w dziennikach powiedzieć dlaczego. Chciałem zrobić coś takiego na formularzu:

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

(strona to nazwa modelu, obraz to nazwa atrybutu, w którym znajduje się załącznik)

Ale jak mogę wykryć to pole wyboru, a co ważniejsze, jak usunąć obraz? Doceniam każdą pomoc!

jyoseph
źródło

Odpowiedzi:

104

Po pierwsze, kiedy tworzysz check_box w form_for (wygląda na to, że jesteś), to formularz powinien domyślnie wysłać: image_delete jako „1” jeśli zaznaczone i „0” jeśli odznaczone. Deklaracja metody wygląda następująco:

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

Co pokazuje, że możesz przypisać inne wartości, jeśli chcesz, ale jest to oczywiście opcjonalne.

Po drugie, wezwanie do ręcznego usunięcia załącznika bez usuwania instancji modelu, do którego jest on dołączony, to:

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

Aby zrealizować swój sposób usuwania obrazów za pomocą pola wyboru, być może dodaj coś takiego do modelu strony:

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

W ten sposób po utworzeniu formularza i dodaniu pola wyboru: image_delete załaduje on domyślną wartość „0” z instancji użytkownika. A jeśli to pole jest zaznaczone, to kontroler zaktualizuje image_delete do "1" i gdy użytkownik zostanie zapisany, sprawdzi, czy obraz ma zostać usunięty.

DanneManne
źródło
W tym przykładzie, czy Page # image odwołuje się do innego modelu has_attached_fileo nazwie image, do którego lub ma go Page ma załącznik?
John Bachir,
@page to zmienna modelowa, która has_attached_file: image, ale z jakiegoś powodu nazwałem modelowego User. Zmienię i zaktualizuję, aby wyjaśnić.
DanneManne
Okay, to ma więcej sensu :)
John Bachir,
Nie rozumiem, dlaczego nie robisz tam po prostu self.image.destroy - czy wyczyść usuwa podstawowy plik, ale zachowuje meta informacje o obrazie w modelu Page? Dlaczego chcesz to zrobić? (i nie wygląda na to, że tego chce zrobić osoba zadająca pytanie)
John Bachir,
11
To podejście również zadziałało dla mnie ... ale napotkałem jeden problem ... jeśli użytkownik zaznaczy pole wyboru image_delete i jednocześnie doda nowy obraz w formularzu, to stary obraz zostanie usunięty, a nowy nie zostanie zapisany . Rozwiązałem to przez zmianę warunek self.image.clear if @image_delete == "1" and !image.dirty?w destroy_image?metodzie
Zeeshan
97

has_attached_file :asset

=>

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

Nie ma potrzeby niszczenia zasobów, zrobi to Paperclip.

W formie form.check_box(:delete_asset)wystarczy.

Benoit B.
źródło
3
To działa i jest prostsze niż odpowiedź IMHO @DanneManne. Bardzo dobrze! :)
MetalElf0
Jak byś napisał dla tego specyfikację?
Hengjie
1
Dzięki ! Aby pomóc mi jeszcze bardziej odchudzić to: has_attached_file :asset has_destroyable_file :asset stworzyłem inicjator do dodania do config/initializers/ gist.github.com/3954054
Sunny
2
Znalazłem problem z tą metodą przynajmniej poprzez accepts_nested_attributes. Funkcja before_validation nie jest wyzwalana przy zagnieżdżonym zapisie, jeśli żadne inne atrybuty nie zostały zmienione. Zobacz moją odpowiedź poniżej na rozwiązanie
Paul Odeon
4
@SurgePedroza Uważam, że musisz zezwolić na parametr: delete_asset, patrz guide.rubyonrails.org/ ...
sman591
12

To jest odpowiedź Benoit, ale zapakowana w moduł i obejmująca skrajny przypadek zagnieżdżonych modeli atrybutów, w których pole wyboru zniszczenia jest jedyną zmianą w modelu.

Będzie dotyczyć wszystkich załączników w modelu.

# 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
źródło
1
Nie wiem, dlaczego nie zwróciło to większej uwagi. attachment_definitionswłaśnie uratował mi życie.
56 tys.
Potrzebuje też tej linii:attr_accessible :"delete_#{name}"
56 tys.
2
Powyższy przykład powinien znajdować się w twoich obawach lub folderze modelu. W modelu, w którym chcesz, po prostu dodaj linię include DeletableAttachmentpod dowolnymi has_attached_filestwierdzeniami
Paul Odeon
2
W rails3 będziesz potrzebować attr_accessible: "delete _ # {name}" również
Mateu
1
Pamiętaj, aby zezwolić, :delete_<your_attribute>jeśli używasz silnych parametrów w kontrolerze
ivanxuu
5

pamiętaj, aby dodać to również do swojego modelu strony:

attr_accessible :image_delete
Glenn McW
źródło
1

Zmodyfikowana wersja rozwiązania Paula do obsługi niestandardowych atrybutów Rails 5. Chciałbym tylko, żeby był sposób na umieszczenie modułu na początku pliku, przed has_attached_filedefinicjami.

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
źródło
0

Udało mi się to osiągnąć przy mniejszej ilości kodu, po prostu implementując a delete_attachmentpo stronie modelu .:

class MyModel < ApplicationRecord
  has_attached_file :image

  def image_delete=(other)
    self.image = nil if other == "1" or other == true
  end
end
stwienert
źródło