2

AR Models

class Post < ActiveRecord::Base
  has_one :sponsor
  accepts_nested_attributes_for :sponsor
end

class Sponsor < ActiveRecord::Base
  has_attached_file :logo
  belongs_to :post
end

Controller

def update
  if @post.update(post_params)
    flash[:success] = 'Post update sucessfully'
  else
    flash[:error] = @post.errors.full_messages.join(', ')
    render :edit
  end
end

def post_params
  params.require(:post).permit(:title, :content, sponsor_attributes: [:descriptive_text, :logo, :name, :website, :description])
end

Here when a Post is updated, sponsor also gets updated in nested form.

But while updating if user doesn't choose any image, Paperclip removes existing attachment.

How can we preserve existing attachment if user doesn't select other while updating a record?

Amit Patel
  • 15,609
  • 18
  • 68
  • 106

1 Answers1

3
accepts_nested_attributes_for :sponsor, reject_if: :all_blank

reject_if.

The problem you have is that, unfortunately, Paperclip is actually quite "dumb" when it comes to accepting data.

As many people send associated data to Paperclip, it basically takes what it's given and builds objects out of it. In your case, it means you're sending blank objects, leading Paperclip to replace your existing attachment with the blank one.

The reject_if switch for accepts_nested_attributes_for is the fix for this -- it allows you to specify any occasions whence Rails will "reject" any nested data, preserving the file you have...

reject_if

Allows you to specify a Proc or a Symbol pointing to a method that checks whether a record should be built for a certain attribute hash.

The hash is passed to the supplied Proc or the method and it should return either true or false. When no :reject_if is specified, a record will be built for all attribute hashes that do not have a _destroy value that evaluates to true.

Passing :all_blank instead of a Proc will create a proc that will reject a record where all the attributes are blank excluding any value for _destroy.


The caveat to this would be if you were updating something other than the image (I see you have :descriptive_text, :logo, :name, :website, :description).

In this instance, you'd need to pass the appropriate data to your Sponsor model (IE no logo param):

def post_params
   sponsor_params = [:id, :descriptive_text, :logo, :name, :website, :description]
   sponsor_params -= [:logo] if (action_name == "update") && !params[:post][:sponsor_attributes][:logo]

   params.require(:post).permit(:title, :content, sponsor_attributes: sponsor_params)
end

I'm sure there's a better way to do this with the Paperclip validators, but for now the above should suffice.


References:

Community
  • 1
  • 1
Richard Peck
  • 76,116
  • 9
  • 93
  • 147
  • 1
    It works. Thanks a lot. You saved my day. I have modified `post_params` method in your answer a bit to make it work. – Amit Patel Oct 21 '15 at 09:55