19

I'm having trouble with carrierwave and rails 4 strong parameters. I have a very simple model with a carrier wave upload button. I'd like to show an error message if someone submits the upload form without choosing a file to upload.

Right now, I get a param not found:photo error with this message:

  # Never trust parameters from the scary internet, only allow the white list through.
    def photo_params
      params.require(:photo).permit(:image)
    end

This error is happening because Rails 4's strong parameters is requiring that the image parameter be present to submit the form, but it's not there because the user hasn't selected an image.

I can't figure out a way to work around this and have it redirect to the same action and display an error message.

Is there a way to do this with strong parameters?

Here's the development log when I try to use the form without a photo selected: https://gist.github.com/leemcalilly/09b6166ce1af6ca6c833

And here's the development log when I choose a photo and it uploads successfully: https://gist.github.com/leemcalilly/1f1e584f56aef15e7af1

Other relevant files: * models/photo.rb - https://gist.github.com/leemcalilly/95c54a5df4e4ee6518da * controllers/photos_controller.rb - https://gist.github.com/leemcalilly/c0723e6dc5478b0f914d * uploaders/image_uploader.rb - https://gist.github.com/leemcalilly/5e43f6524734991773ae * views/photos/index.html.erb - https://gist.github.com/leemcalilly/a8c4c808e5e8a802933b * views/photos/_form.html.erb - https://gist.github.com/leemcalilly/cd0fd518c1b47d9bfb62 * initializers/carrierwaver.rb - https://gist.github.com/leemcalilly/49e04fa1dda891dd108b

Lee McAlilly
  • 9,084
  • 12
  • 60
  • 94
  • 1
    Can you provide your `_form` code? – GoGoGarrett Jul 30 '13 at 03:59
  • Sure. Forgot to include that https://gist.github.com/leemcalilly/cd0fd518c1b47d9bfb62 – Lee McAlilly Jul 30 '13 at 12:51
  • 1
    And can you show your post code to the controller? It seems that the params does not include the photo key. To simply debug you could do `params.permit!` to allow anything, or to simplify the process you can do something along the lines of `params.permit(:image)` – GoGoGarrett Jul 31 '13 at 11:51
  • I have this line in my photos_controller.rb (see above in the original question): `params.require(:photo).permit(:image)`. Is that not what you're suggesting? – Lee McAlilly Jul 31 '13 at 16:05
  • If I switch that line to `params.permit!` I still get the same error: `param not found: photo` – Lee McAlilly Jul 31 '13 at 21:23
  • If I switch that line to `params.permit(:image)` then I get an `Image can't be blank` validation error message, but no rails error. It renders the correct template with the `image can't be blank` message. That seems like progress, but when I try to upload an image through the form, it gives me the same validation error. – Lee McAlilly Jul 31 '13 at 21:30
  • What is the form actually posting to the controller? There should be some kind of hash being output in the server logs when it's posted. Can you post that here? – Josh Kovach Aug 01 '13 at 20:03
  • Yeah, just added it to my question https://gist.github.com/leemcalilly/09b6166ce1af6ca6c833 – Lee McAlilly Aug 01 '13 at 20:15
  • What does it look like when you post with a file? – Josh Kovach Aug 02 '13 at 00:06
  • it posts fine. the dev log looks like this: https://gist.github.com/leemcalilly/1f1e584f56aef15e7af1 – Lee McAlilly Aug 02 '13 at 00:26
  • To solve the `Image can't be blank` issue, you may need to specify `storage :file` in the uploader. – Josh Kovach Aug 02 '13 at 00:27
  • Hmmm. that's not an option because heroku won't let me host the images on the server. They have to go to S3. I guess I was wondering if there's a way around this so that the user doesn't see a nasty rails error and gets some sort of useful error message. – Lee McAlilly Aug 02 '13 at 00:54
  • I was under the impression that you were doing this locally. You can set it to use `storage :fog` in that case. Either way, I think you need to set storage to something. Just make sure you have everything configured for S3 and that `fog` is in your Gemfile, too. You could also set it conditionally like `storage Rails.env.production? ? :fog : :file` – Josh Kovach Aug 02 '13 at 02:14
  • I am using s3 in development to more closely match my production environment. You're right I was missing storage :fog in image_uploader.rb. I added that but I'm still getting the same `param not found: photo` error. – Lee McAlilly Aug 02 '13 at 12:50
  • Does it work now if you do `params.permit(:image)`? No more `Image can't be blank` message? – Josh Kovach Aug 02 '13 at 15:30
  • If I switch my photos controller to this `params.permit(:image)` (https://gist.github.com/leemcalilly/88839f37eb7ce0f3deba), I get an Image can't be blank error if an image is present and when it is not present. – Lee McAlilly Aug 02 '13 at 15:43
  • not sure if you've seen this link, if you do I'll remove it: http://stackoverflow.com/questions/14483963/rails-4-0-strong-parameters-nested-attributes-with-a-key-that-points-to-a-hash – rmagnum2002 Aug 02 '13 at 20:35
  • No I haven't seen that link. That looks like the right path. I'll try it. – Lee McAlilly Aug 02 '13 at 20:37
  • yeah I'm not able to get that to work – Lee McAlilly Aug 02 '13 at 20:52

2 Answers2

55

The rails guides provides the solution. Use the fetch method in the same way as the require method. Note a second argument: it supplies the default value. So, if the params[:photo] would not be found, the default value (an empty hash, as of the example below) will be returned:

params.fetch(:photo, {})
shybovycha
  • 11,556
  • 6
  • 52
  • 82
Mrjaco12
  • 776
  • 5
  • 4
12

Sorry spotted this is bit late ,Ok now looking at your form code which look something like this

<%= form_for @photo, :html => {:multipart => true}  do |f| %>
  <% if @photo.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@photo.errors.count, "error") %> prohibited this photo from being saved:</h2>

      <ul>
      <% @photo.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <fieldset>
    <legend>Upload a Photo</legend>
    <div class="field">
      <%= f.file_field :image %>
    </div>              
   </fieldset>  

   <%= f.submit "Upload Photo", :class => "btn btn-small" %>
<% end %>

Now there this is not a problem with rails or carrierwave or strong_parameter it is something how html works . It like this, if you have file input and if nothing is attached to it the name and value pair is not sent to the server by HTML think of it something like checkbox or disabled field

Now since your form only contain <%= f.file_field :image %> and it does not contain any other field (attributes of photo model)

therefore the photo hash would not get constructed when the file input does not contain any attachment

which is evident in your log as well

Parameter (without attachment attached) =>

{"utf8"=>"✓", "authenticity_token"=>"IvOieBvxdA6qzVKrt1dYBuCXlt+uuWCyWjUAuTK0XEU=", "commit"=>"Upload Photo"}

Parameter (with attachment attached) =>

 {"utf8"=>"✓", "authenticity_token"=>"I4O0w+Wk8nJaD6HJRSC+FfuAip5NVE6TkCUFDEN+sW0=", "photo"=>{"image"=>#<ActionDispatch::Http::UploadedFile:0x007fc730b5de08 @tempfile=#<Tempfile:/var/folders/0m/n32lgww55vzf5pfc4kh17gm00000gr/T/RackMultipart20130801-32602-1nj6u2b>, @original_filename="bag.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"photo[image]\"; filename=\"bag.jpg\"\r\nContent-Type: image/jpeg\r\n">}, "commit"=>"Upload Photo"}

now you can see the difference between the two and that the difference is the reason of your error because in

1st case

params[:photo].present? => false

2nd case

params[:photo].present? => true

and hence when you do this params.require(:photo).permit(:image) the code throws the errors

because of the line mention that .require(:photo) which is not present in params

Solution:-

Perhaps you can do something like this

   def photo_params
     if params[:photo].present?
      params.require(:photo).permit(:image)
     end
   end

Conclusion :-

It not anyone fault because that is how the HTML work if no attachment no name=value pair submitted to server and because of which photo params is not sent to server hence the hash is does not have them and hence strong parameter throws the error

Hope this help

Viren
  • 5,812
  • 6
  • 45
  • 98
  • This is very helpful and gets at the heart of the problem, but I think I need to accept Mrjaco12's answer as a more canonical approach since it's documented in the rails guides. Thanks for your help though! – Lee McAlilly Aug 05 '13 at 20:16
  • what if we need "if photo_params[:image].present?" in this case photo_params is false, and [:image] hash is nil. so we cant check for present? – Kirka121 Feb 24 '15 at 15:22
  • @Kirka121 Sorry man I seriously don't remember much on this but just a question how come photo_params will be `false` – Viren Feb 24 '15 at 16:18