Paperclip displaying multiple file uploads - ruby-on-rails

I have multiple files being uploaded using Paperclip, but I am having trouble displaying them. Here is what I'm trying:
Model(s):
class Attach < ActiveRecord::Base
attr_accessible :protocol_id, :file
has_attached_file :file,
:path => ':rails_root/public/system/attachs/files/000/000/0:id/original/:basename.:extension'
attr_accessible :file_file_name, :file_content_type, :file_file_size
validates_attachment_presence :file
belongs_to :protocol
end
class Protocol < ActiveRecord::Base
attr_accessible :current_approved, :p_irb_apn, :past_approved, :attachs_attributes
has_many :attachs
accepts_nested_attributes_for :attachs, :allow_destroy => true
end
Part of my controller:
def new
#protocol = Protocol.new
#protocol.attachs.build
respond_to do |format|
format.html # new.html.erb
format.json { render json: #protocol }
end
end
Part of my form where I store images:
<div class="field">
<%= f.label :file, "Mod" %>
<%= file_field_tag('protocol_attachs_attributes_file', multiple: true, name: "protocol[attachs_attributes][][file]") %>
</div>
And my show:
<p>
<b>Modification:</b>
<% for attach in #protocol.attachs %>
<%= link_to "Download", #protocol.attachs.url(:original)%>
<% end %>
</p>
I'm getting the same file over and over, every time I upload (even if it's a different file). Can anyone assist me with this issue?

You're iterating, but running the same code for each attachment link.
Using canonical Ruby, it would be closer to:
<% #protocol.attachs.each do |attach| %>
<%= link_to "Download", attach.url(:original) %>

Related

rails file_field not showing

I would like to make my app upload multiple files with Shrine, but one doc suggests two file_fields whereas the other suggests only one. After posting a question to their discourse forum, it was suggested that I hide the one named files[]. Whether I do this or not, the first file_field always fails to render. Why does this field not display?
<%= form_for #item, html: { enctype: "multipart/form-data" } do |f| %>
<%= f.fields_for :photos do |i| %>
<%= i.label :image %>
<%= i.hidden_field :image, value: i.object.cached_photos_data, class: "upload-data" %>
<%= i.file_field :image, class: "upload-file" %> /// why is this not rendering?😢
<% end %>
<%= file_field_tag "files[]", multiple: true %> // what purpose does this one serve?
<%= f.text_field :title %>
<%= f.submit "Submit" %>
<% end %>
Models:
class Item < ApplicationRecord
has_many :photos, as: :imageable, dependent: :destroy
end
class Photo < ApplicationRecord
include ImagesUploader::Attachment(:image)
belongs_to :imageable, polymorphic: true
validates_presence_of :image
end
Controller:
class ItemsController < ApplicationController
def new
#item = current_user.items.new
end
def create
#item = current_user.items.create(item_params)
#item.save
end
private
def item_params
params.require(:item).permit(:title, photos_attributes: { image: [] })
end
end
Read the first link carefully: It says that the single field (i.file_field :image) is used to display existing images (which is why it's wrapped in f.fields_for :photos in the example) and the multiple field (file_field_tag "files[]", multiple: true) is used to upload new files. So if your #item doesn't have an :image, then the field isn't shown.
Let me know if this needs any further clarification – happy to help!

rails 4 unpermitted params nested form

I know there are alot of these kinds of posts on SO, and I think Ive read and tried each of them, all without success.
I have Post and Image models that I need to work together with a many to one relationship.
class Post < ActiveRecord::Base
has_many :images
end
class Image < ActiveRecord::Base
belongs_to :post
mount_uploader :file, images_uploader
end
Here is the post_parms declaration in my posts controller, which includes all of the fields in my image model migration.
private
def post_params
params.require(:post).permit(:title, :content, image_attributes: [:id, :post_id, :file])
end
and here is my post creation form where, I hope to allow multiple image asset creation with each post.
<%= form_for #post, html: {class: "pure-form pure-form-stacked"} do |post| %>
<%= post.fields_for :image, :html => { :multipart => true } do |image| %>
<%= image.label :file, "Upload Image:" %>
<%= image.file_field :file, multiple: true %>
<% end %>
<fieldset class="post-form">
<%= post.label :title %>
<%= post.text_field :title %>
<%= post.label :content %>
<%= post.text_area :content, :class => "redactor", :rows => 40, :cols => 120 %>
</fieldset>
<div class = "button-box">
<%= post.submit class: "pure-button pure-button-primary" %>
<%= link_to "Cancel", posts_path, :class => "pure-button" %>
</div>
Despite repeated effort and reading every post I can find on this topic, I'm still getting:
Unpermitted parameters: image
The trouble here is that this error provides no clue where to start looking for the problem. Since Im not really sure where to look next I thought I would post it here looking for more professional opinions.
Update the Post model as below:
class Post < ActiveRecord::Base
has_many :images
accepts_nested_attributes_for :images ## Add this
end
This way upon form submission you would receive the image attributes in key images_attributes and not image which you are currently receiving which is causing the warning as Unpermitted parameters: image
As you have 1-M relationship between Post and Image
You need to update post_params as below:
def post_params
params.require(:post).permit(:title, :content, images_attributes: [:id, :post_id, :file])
end
Use images_attributes (Notice plural images) and NOT image_attributes (Notice singular image)
And change fields_for in your view as
<%= post.fields_for :images, :html => { :multipart => true } do |image| %>
Use images (Notice plural) and NOT image (Notice singular)
UPDATE
To resolve uninitialized constant Post::Image error
Update Image model as below:
class Image < ActiveRecord::Base
belongs_to :post
## Updated mount_uploader
mount_uploader :file, ImagesUploader, :mount_on => :file
end
Also, suggested to remove multiple: :true from
<%= ff.file_field :file, multiple: true %>

Carrierwave, Rails 4, and Multiple Uploads

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
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.
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.
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: [] })
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

carrierwave + nested models: file_field not calling "create" method

I am trying to follow Railscast Ep 381 to integrate multi-file upload into my Rails app. For my app, the Projects model has many Steps, and Steps have many Images. The Image model is using a Carrierwave uploader.
Right now, I'm able to upload single image files from within the edit_steps path. But I'm trying to design the upload interface so that a gallery on the edit_steps page automatically refreshes once new images have been added (so once images have been selected but the form has not yet been submitted).
According to the Railscast episode, when you select images, they should automatically call the create action for each file. But my application is clearly not doing that. How can I ensure that the create method of the Image model is being called?
step.rb
class Step < ActiveRecord::Base
extend FriendlyId
friendly_id :number
attr_accessible :description, :name, :number, :project_id, :images_attributes
belongs_to :project
has_many :images
accepts_nested_attributes_for :images, :allow_destroy => :true
end
image.rb
class Image < ActiveRecord::Base
attr_accessible :project_id, :step_id, :file, :caption
belongs_to :step
belongs_to :project
mount_uploader :file, FileUploader
end
views/steps/edit.html.erb
%= semantic_form_for [#project, #step], :html => {:multipart => true} do |f| %>
<%= f.inputs do%>
....
<div id="images">
<%= render :partial => 'images/image', :collection => #step.images.all %>
</div>
<div class="clear"></div>
....
<%= f.fields_for :images, Image.new do |image_form| %>
<%= image_form.file_field :file, multiple: true, name: "image[file]"%>
<%end%>
javascript/images.js.coffee
jQuery ->
$('#new_image').fileupload
dataType: "script"
controllers/images.controller
def create
#image = Image.create(params[:image])
end
images/create.js.erb
<% if #image.new_record? %>
alert("Failed to upload image: <%= j #image.errors.full_messages.join(', ').html_safe %>");
<% else %>
alert("adding images")
$("#images").append("<%= j render(#image) %>");
<% end %>

Rails Paperclip & Multiple File Uploads

I am looking for a solution to give the user the ability to upload multiple images through one file_field. I have looked in to options such a Jquery File Upload and Uploadify but have yet to come across good examples with a working solution.
I already have multiple images setup,
has_attached_file :asset,
:styles => { :large => "640x480", :medium => "300x300", :thumb => "100x100" },
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml",
:path => "/:contributor_id/:listing_name/:filename"
Right now I am displaying 5 individual file_fields
def new
#listing = Listing.new
5.times {#listing.assets.build }
respond_to do |format|
format.html # new.html.erb
format.json { render json: #listing }
end
end
I would like to have
<%= f.file_field :asset, :multiple => true %>
That allows the user to select multiple files in their file browser. But how can I process these with a nested model? And get them to upload.
So there are a few issues here.
First, Paperclip's has_attached_file method isn't an association to many files. It looks like you're trying to build an "asset" as if it's a Rails association. All Paperclip does is put a couple of fields into your table to store some meta-data about the file and you get one attached file per declaration of has_attached_file. If you want to attach 5 files, you would need to do something like:
has_attached_file :asset1
has_attached_file :asset2
has_attached_file :asset3
has_attached_file :asset4
has_attached_file :asset5
OR, alternatively, you could create another model just to store the files. For example:
class Listing < ActiveRecord::Base
has_many :assets
end
class Asset < ActiveRecord::Base
belongs_to :listing
has_attached_file :picture
end
That way, you could have multiple assets attached to one listing (you didn't say what the original object was so I just called it "listing").
Second, there is no such thing as a multiple file upload in HTML (and, as such, the file_field method doesn't take a :multiple => true argument. You'll have to use something beyond Rails built-in form handling if you want multiple-file upload. Uploadify is a decent choice (that I've used before). There is a gem that will transform file fields to use uploadify (and will support the :multiple => true syntax that you want): https://github.com/mateomurphy/uploadify_rails3/wiki. However, I cannot vouch for how good it is.
My advice would be to start step-by-step. Uploading via Flash to Rails can be a complicated process that involves dealing with the CSRF meta-tag and other fields in your form. Start by making a form that allows a user to upload one file and stores it through Paperclip. Then maybe break the has_attached_file declaration into another model so that you can have 1 or many files associated with a model (as shown in the multi-model code block above). Then try adding Uploadify or another alternative. Ernie Miller has a decent tutorial on integrating Uploadify: http://erniemiller.org/2010/07/09/uploadify-and-rails-3/.
To start, remember that has_attached_file can only attach one file. When you try calling #listing.assets there is no "assets". There is an asset. You need to create a separate model yourself and use Rails' associations if you want multiple files.
Accepted answer says there is no such thing as a multiple file upload in HTML.
<%= f.file_field :files, multiple: true %>
This allows you to select multiple images and send them as an array.
If you have the relationship Dog has_many Images and Image has_attachment :file, do this to get multiple images to upload at once:
In your html.erb
<%= form_for #dog, html: { multipart: true } do |f| %>
<%= f.file_field :files, accept: 'image/png,image/jpeg,image/gif', multiple: true %>
<%= end %>
In your controller
def dog_params
params.require(:dog).permit files: []
end
In your Dog model
def files=(array = [])
array.each do |f|
images.create file: f
end
end
This is assuming you're already able to upload one image but want to upgrade to multiple images at once. Notice that wait time will increase.
To help reduce wait time, peep my post on this question related to speed uploading.
Here is a full example of multiple file uploads. Here a user has_many uploads. Each upload model has an avatar which represents the file attachment. Ultimately: we are creating many uploads when we create the user.
The Models
#models/user.rb
class User < ApplicationRecord
has_many :uploads
def files=(array_of_files = [])
array_of_files.each do |f|
uploads.build(avatar: f, user: self)
end
end
end
#models/upload.rb
class Upload < ApplicationRecord
belongs_to :user
has_attached_file :avatar
validates_attachment_content_type :avatar, :content_type => ["image/png"]
end
The form:
# views/users/_form.html.erb
<%= form_with(model: user, local: true) do |form| %>
...
<div class="field">
<%= form.file_field :files, multiple: true %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
The Controller
class UsersController < ApplicationController
before_action :set_user, only: [:show]
def show
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to #user, notice: 'User was successfully created.'
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:name, files: [])
end
end
User#show
<p id="notice"><%= notice %></p>
<p>
<strong>Name:</strong>
<%= #user.name %>
</p>
<h3>Uploads</h3>
<div>
<% #user.uploads.each do |upload| %>
<div>
<%= link_to upload.avatar.url do%>
<%= upload.avatar_file_name %>
<% end %>
</div>
<% end %>
</div>
<%= link_to 'Edit', edit_user_path(#user) %> |
<%= link_to 'Back', users_path %>

Resources