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? || !@page.image? -%>
    <%= 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!

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.


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.


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

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

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.