Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Paperclip (3.0) and Rails (3.2): f.file_field loses file path on 'new' action after a validation error

I set up a story model with an image attachment handled by Paperclip, that looks like:

class Story < ActiveRecord::Base  
  has_attached_file :image # [...]
  attr_accessible :user_id, :title, :image, :image_file_name
  belongs_to: user

  validates_presence_of :user_id
  validates :title,     :length => { :maximum => 50 }
  validates_attachment_size :image, :less_than => 2.megabytes, :unless => Proc.new { |story| story[:image].nil? }
  # [...] 
end

When I fill my story form, that looks like:

<%= form_for @story, html: { multipart: true } do |f| %>
<% if @story.errors.any? %>
<div id="error-explanation">
    <ul>
        <% @story.errors.full_messages.each do |msg| %>
        <li class="error-mess">Error: <%= msg.downcase %></li>
        <% end %>
    </ul>
</div>
<% end %>

<%= f.text_field :title %></td>
<%= f.file_field :image %>
<%= f.submit t('.send') %>

<% end %>

If validation fails for a story.title too long the form is redisplayed correctly along with the proper error message and the invalid title already filled in, but the file_field is now blank and I have to click again on it in order to re-select the file I want to upload.

And here is how my stories_controller.rb looks like:

def create
  @story = @current_user.stories.new(params[:story]) 
  if @story.save                                                                                                                   
    redirect_to thanks_path    
  else
    # [email protected] so I render action 'new' again just to
    # bang my head against this 'anomaly' 
    render action: "new"
  end              
end

How can I avoid users having to re-select the file to upload after a validation error?

like image 399
Darme Avatar asked Jun 22 '12 09:06

Darme


2 Answers

The way HTTP file uploads work in browsers, the file has been uploaded to your app already on first submit – so you should store it somewhere, so that you still have access to it later on a second form submit. (At least in PHP an uploaded file get’s deleted after the script has run, if it’s not explicitly been moved somewhere else – I don’t know if that applies for RoR as well.)

You can’t pre-fill an input type=file field in HTML – for security reasons. And even if the user picks the file again, they would have to send it a second time – waste of user’s and your bandwidth.

So either store it somewhere on first submit, or try to do your validations on the client-side too with JavaScript before allowing submit (as far as possible), so that you minimize the form submits that actually fail validation on the server side.

like image 161
CBroe Avatar answered Sep 28 '22 09:09

CBroe


CBroe is right, the best solution is to store the file temporarily. What I would do to do this is: - Move the file to a temp directory, and name it with the id of the user that was trying to upload it. - When the form is posted and there is no file uploaded, try to use the temp file for that user (if it exists). - If the Story is succesfully saved, delete any temp files for that user.

I think that should do the trick.

like image 44
iwiznia Avatar answered Sep 28 '22 09:09

iwiznia