Not losing paperclip attachment when model cannot be saved due to validation error

peanutbutter picture peanutbutter · Mar 4, 2011 · Viewed 7.1k times · Source

The scenario is a normal model that contains a paperclip attachment along with some other columns that have various validations. When a form to to create an object cannot be saved due to a validation error unrelated to the attachment, columns like strings are preserved and remain prefilled for the user, but a file selected for uploading is completely lost and must be reselected by the user.

Is there a standard approach to preserving the attachment in the case of a model validation error? This seems like a very common use case.

It seems inelegant to hack up a solution where the file is saved without an owner and then reconnected to the object after it's successfully saved so I'm hoping to avoid this.

Answer

John Gibb picture John Gibb · Apr 15, 2011

Switch to using CarrierWave. I know this was in a comment, but I just spent all day making the transition so my answer may be helpful still.

First you can follow a great railscast about setting up carrier wave: http://railscasts.com/episodes/253-carrierwave-file-uploads

To get it to preserve the image between posts, you need to add a hidden field with the suffix 'cache':

<%= form_for @user, :html => {:multipart => true} do |f| %>
  <p>
    <label>My Avatar</label>
    <%= f.file_field :avatar %>
    <%= f.hidden_field :avatar_cache %>
  </p>
<% end %>

For Heroku

And if you're deploying to Heroku like I am, you need to make some changes to get it to work, since the caching works by temporarily saving uploads in a directory called public/uploads. Since the filesystem is readonly in Heroku, you need to have it use the tmp folder instead, and have rack serve static files from there.

Tell carrierwave to use the tmp folder for caching.

In your config/initializers/carrierwave.rb (feel free to create if not there), add:

CarrierWave.configure do |config|
  config.root = Rails.root.join('tmp')
  config.cache_dir = 'carrierwave'
end

Configure rack to serve static files in from the tmp/carrierwave folder

In your config.ru file, add:

use Rack::Static, :urls => ['/carrierwave'], :root => 'tmp'

For an example of a fully functional barebones rails/carrierwave/s3/heroku app, check out:

https://github.com/trevorturk/carrierwave-heroku (no affiliation, just was useful).

Hope this helps!