13

I have been banging my head against the wall trying to get Carrierwave, Rails 4, and Multiple Uploads all working together. I can get a single file upload working just fine as in this and many other projects.

This is not a nested situation - just simply uploading to a single model called Transcription and wanting to create a record for each document uploaded.

I cannot seem to find the correct way to declare the "document" field used for the carrierwave mount

mount_uploader :document, DocumentUploader

as an array for the strong parameters to recognize.

I have tried whitelisting: whitelisted[:document] = params[:transcription]['document'],

declaring the "document" as an array:

params.require(:transcription).permit(..... ,:document => [])

params.require(:transcription).permit(..... , { document: [] })

This all seems more like I am declaring the array for a nested model, but I really want Rails 4's strong parameters to simply see the "document" array created by the file_field, :multiple => true

ie. from the log: form-data; name=\"transcription[document][]

Has anybody successfully accomplished multiple uploads in Rails 4 with strong parameters? If so would you please share?

Thanks...

Cheers,

Bill

Koen.
  • 25,449
  • 7
  • 83
  • 78
kelleysislander
  • 131
  • 1
  • 5

4 Answers4

36

This is solution to upload multiple images using carrierwave in rails 4 from scratch

To do just follow these steps.

rails new multiple_image_upload_carrierwave

In gem file

gem 'carrierwave'
bundle install
rails generate uploader Avatar 

Create post scaffold

rails g scaffold post title:string

Create post_attachment scaffold

rails g scaffold post_attachment post_id:integer avatar:string

rake db:migrate

In post.rb

class Post < ActiveRecord::Base
   has_many :post_attachments
   accepts_nested_attributes_for :post_attachments
end

In post_attachment.rb

class PostAttachment < ActiveRecord::Base
   mount_uploader :avatar, AvatarUploader
   belongs_to :post
end

In post_controller.rb

def show
   @post_attachments = @post.post_attachments.all
end

def new
   @post = Post.new
   @post_attachment = @post.post_attachments.build
end

def create
   @post = Post.new(post_params)

   respond_to do |format|
     if @post.save
       params[:post_attachments]['avatar'].each do |a|
          @post_attachment = @post.post_attachments.create!(:avatar => a, :post_id => @post.id)
       end
       format.html { redirect_to @post, notice: 'Post was successfully created.' }
     else
       format.html { render action: 'new' }
     end
   end
 end

 def update
   respond_to do |format|
     if @post.update(post_params)
       params[:post_attachments]['avatar'].each do |a|
         @post_attachment = @post.post_attachments.create!(:avatar => a, :post_id => @post.id)
       end
     end
  end

  def destroy
    @post.destroy
    respond_to do |format|
      format.html { redirect_to @post }
      format.json { head :no_content }
    end
  end


 private
   def post_params
      params.require(:post).permit(:title, post_attachments_attributes: [:id, :post_id, :avatar])
   end

In views/posts/_form.html.erb

<%= form_for(@post, :html => { :multipart => true }) do |f| %>
   <div class="field">
     <%= f.label :title %><br>
     <%= f.text_field :title %>
   </div>

   <%= f.fields_for :post_attachments do |p| %>
     <div class="field">
       <%= p.label :avatar %><br>
       <%= p.file_field :avatar, :multiple => true, name: "post_attachments[avatar][]" %>
     </div>
   <% end %>

   <% if params[:controller] == "post" && params[:action] == "edit" %> 
     <% @post.post_attachments.each do |p| %>
       <%= image_tag p.avatar, :size => "150x150" %>
     <% end %>
   <% end %>

   <div class="actions">
     <%= f.submit %>
   </div>
<% end %>

In views/posts/show.html.erb

<p id="notice"><%= notice %></p>

<p>
  <strong>Title:</strong>
  <%= @post.title %>
</p>

<% @post_attachments.each do |p| %>
  <%= image_tag p.avatar_url, :size => "150x150" %>
  <%= link_to "Destroy", p, method: :delete %>
<% end %>

<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>

In rails 3 no need to define strong parameters and as you can define attribute_accessible in both the model and accept_nested_attribute to post model because attribute accessible is deprecated in rails 4.

Koen.
  • 25,449
  • 7
  • 83
  • 78
SSR
  • 6,398
  • 4
  • 34
  • 50
  • Great post @SSR, only multiple upload I've managed to get working. Don't suppose you'd be able to expand it slightly? In it's current form the files aren't deleted when the parent model is. (Or don't for me), and I'm wondering how I'd go about editing the post and it's attachments together. – Darkstarone Feb 18 '14 at 22:42
  • Essentially I'm wondering what the post_controller.rb would look like to correctly edit and delete the attachments? – Darkstarone Feb 18 '14 at 23:19
  • 2
    @Darkstarone : Thanks for compliment. :) Have updated my answer for edit method. – SSR Feb 24 '14 at 15:17
  • 2
    Updated my answer with destroy method. – SSR Feb 24 '14 at 15:40
  • Thank you for that! Interestingly if you delete the a post with attachments, the attachments remain in the uploads folder. Is there anyway to fix that? – Darkstarone Feb 25 '14 at 00:25
  • use ruby file read/write/delete method to remove files from upload folder. – SSR Mar 09 '14 at 10:13
  • Not sure the purpose of the "accepts_nested_attributes_for :post_attachments" since you're not using it. – davidfurber Jun 05 '14 at 13:07
  • @SSR how would you do validations? (Limit how many images can post, file size, etc) – Loi Huynh Aug 24 '15 at 22:20
1

CarrierWave doesn't support multiple uploads. It's designed to associate a single file with a single field.

If you want multiple uploads, you need either multiple fields (each with a CarrierWave uploader), or multiple objects each with a single CarrierWave uploader field.

The multiple attribute is also unsupported, so if you use it, it's entirely up to you to get the parameters assigned properly.

Taavo
  • 2,406
  • 1
  • 17
  • 17
  • 1
    That is not true. it support multiple upload within single field. see my answer. carrierwave provides much more. – SSR Feb 04 '14 at 09:25
  • 1
    Just to be clear: Your solution uses multiple objects (post_attachments), each with a single uploader, and demonstrates the manual work necessary to get parameters assigned properly for use of the "multiple" attribute. You've demonstrated how to do what the OP wanted, but it doesn't make any part of the above false. – Taavo Mar 29 '14 at 17:57
  • Time progresses ever onwards. The Earth spins and gradually changes, and CarrierWave now supports multiple-file upload natively. However the CarrierWave way of doing it is an ugly hack (referencing all the files in one field using an array) that I would avoid using. Go with SSR's solution instead. – Toby 1 Kenobi Mar 11 '16 at 01:18
0

I would create a model called Documents with a field that's mounted

class Documents < ActiveRecord::Base
  belongs_to :transcription

  mount_uploader :doc, DocumentUploader
end

class Transcriptions < ActiveRecord::Base
  has_many :documents
end

And I would still have user the below line in my controller:

params.require(:transcription).permit(..... , { document: [] })
Tom Prats
  • 7,364
  • 9
  • 47
  • 77
0

The best method for this that I have come across is using the native approach of CarrierWave. If you already have single file upload done, with the native approach it takes less than 5 minutes to get multiple file upload. https://github.com/carrierwaveuploader/carrierwave#multiple-file-uploads

Abass Sesay
  • 833
  • 10
  • 18