【问题标题】:Rails Paperclip how to delete attachment?Rails Paperclip 如何删除附件?
【发布时间】:2011-05-25 01:32:00
【问题描述】:

我在 Rails 3 上使用 Paperclip (w/ Amazon s3)。我想使用更新操作删除现有附件而不替换它

我只找到了这个here 的一个例子,但无法让它工作,它只是不会删除,日志中也没有说明原因。我想在表单上做这样的事情:

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

(page 是模型的名称,image 是保存附件的属性名称)

但是如何检测该复选框,更重要的是,如何删除图像?感谢您的帮助!

【问题讨论】:

    标签: ruby-on-rails ruby paperclip


    【解决方案1】:

    首先,当您在 form_for 中创建一个复选框时(看起来就像您一样),那么默认情况下,如果选中,则表单应将 :image_delete 发送为“1”,如果未选中则发送“0”。方法声明如下所示:

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

    这表明您可以根据需要分配其他值,但这当然是可选的。

    其次,手动删除附件而不删除它所附加到的模型实例的调用是:

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

    为了完成您通过复选框删除图像的方式,也许可以在您的 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
    

    这样,当您创建表单并添加 :image_delete 复选框时,它将从 User 实例加载默认值“0”。如果该字段被选中,则控制器会将 image_delete 更新为“1”,并且当保存用户时,它将检查是否要删除图像。

    【讨论】:

    • 在这个例子中,Page#image 是指另一个 has_attached_file 的模型,还是 Page 有附件,命名为 image?
    • @page 是 has_attached_file :image 的模型变量,但我似乎出于某种原因将模型命名为 User。我会更改并更新以澄清。
    • 好吧,这更有意义:)
    • 我不明白你为什么不只是在那里做 self.image.destroy - 确实清除了底层文件,但在 Page 模型中维护有关图像的元信息?你为什么想做这个? (这似乎不是提问者想要做的)
    • 这种方法也适用于我......但我遇到了一个问题......如果用户选中 image_delete 复选框并同时在表单中添加新图像,那么旧图像是已删除且新图像未保存。我通过在destroy_image? 方法中将条件更改为self.image.clear if @image_delete == "1" and !image.dirty? 解决了这个问题
    【解决方案2】:

    has_attached_file :asset

    =>

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

    无需销毁资产,Paperclip 会做到。

    form.check_box(:delete_asset) 的形式就足够了。

    【讨论】:

    • 这行得通,而且比@DanneManne 回答恕我直言更简单。很好! :)
    • 您将如何为此编写规范?
    • 谢谢!为了帮助我将其进一步精简为:has_attached_file :assethas_destroyable_file :asset 我创建了一个初始化器以添加到config/initializers/ gist.github.com/3954054
    • 我至少通过accepts_nested_attributes发现了这个方法的问题。如果没有更改其他属性,则不会在嵌套保存时触发 before_validation。解决方法见下面我的回答
    • @SurgePedroza 我相信你需要允许参数:delete_asset,见guides.rubyonrails.org/…
    【解决方案3】:

    这是 Benoit 的答案,但包装在一个模块中,并涵盖了嵌套属性模型的边缘情况,其中销毁复选框是模型上唯一更改的内容。

    它将适用于模型上的所有附件。

    # 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
    

    【讨论】:

    • 不知道为什么这没有得到更多的关注。 attachment_definitions刚刚救了我的命。
    • 也需要这条线:attr_accessible :"delete_#{name}"
    • 上面的例子应该在你的关注点或模型文件夹中。在您想要的模型中,只需在任何 has_attached_file 语句下方添加行 include DeletableAttachment
    • 在 rails3 你还需要 attr_accessible :"delete_#{name}"
    • 如果您在控制器中使用强参数,请记住允许:delete_&lt;your_attribute&gt;
    【解决方案4】:

    记得也将它添加到您的页面模型中:

    attr_accessible :image_delete
    

    【讨论】:

      【解决方案5】:

      Paul 解决方案的修改版本,以支持 Rails 5 自定义属性。我只是希望有一种方法可以将模块包含在文件顶部,在 has_attached_file 定义之前。

      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
      

      【讨论】:

        【解决方案6】:

        只需在模型一侧实现delete_attachment,就可以用更少的代码实现这一点。:

        class MyModel < ApplicationRecord
          has_attached_file :image
        
          def image_delete=(other)
            self.image = nil if other == "1" or other == true
          end
        end
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-11-24
          • 2013-12-13
          • 2013-01-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-24
          相关资源
          最近更新 更多