James Osborn
James Osborn

Reputation: 187

Rails 4: Multiple image upload using paperclip

I'm looking to upload multiple images to my 'locations' model. I've called the images model 'assets'. One location has multiple assets. I'm also using paperclip to handle the uploads and nested_form to allow selecting multiple assets.

Weirdly, the locations hash looks to be passing the variables correctly, but they don't appear to be being picked up by the assets model. Any help would be great!

Location model

class Location < ActiveRecord::Base

  has_many :location_post
  has_many :posts, :through => :location_post  
  has_many :assets, dependent: :destroy

  attr_accessor :asset, :assets_attributes
  accepts_nested_attributes_for :assets, :allow_destroy => true 
end

Asset model

class Asset < ActiveRecord::Base

   belongs_to :location

   has_attached_file :asset,
                    :styles => {
                      :blurred => "600x300^",:large => "600x600>", :medium => "250x250^" , :thumb => "100x100^"},
                      #:source_file_options =>  {:all => '-rotate "-90>"'},
                      :convert_options => {
                      :all => '-auto-orient', :blurred => "-blur 0x6 +repage -resize 600x300^" 
                        },
                      :storage => :s3,
                      :s3_credentials => "#{Rails.root}/config/s3.yml",
                      :bucket => "[bucketname]",
                      :path => "/:style/:id/:filename"    

validates_attachment_content_type :asset, :content_type => ["image/jpg", "image/jpeg", "image/png", "image/gif"]

end

Locations Controller

class LocationsController < ApplicationController

...

  def new
    @location = Location.new
    @location.assets.build
    @georesult = Geocoder.search(params[:query])
  end

  def create

    @location = Location.find_or_create_by(name: location_params[:name])


    respond_to do |format|
     if @location.save
       format.html { redirect_to @location, notice: ' <borat voice> Great success! </borat voice>' }
       format.json { render :show, status: :created, location: @location }
     else
       format.html { render :new }
       format.json { render json: @location.errors, status: :unprocessable_entity }
     end
   end
  end

  # PATCH/PUT /locations/1
  # PATCH/PUT /locations/1.json
  def update
    respond_to do |format|
     if @location.update(location_params)
      format.html { redirect_to @location, notice: 'Location was successfully updated.'  }
      format.json { render :show, status: :ok, location: @location }
     else
      format.html { render :edit }
      format.json { render json: @location.errors, status: :unprocessable_entity }
     end
    end
  end

 ...

 private
    # Use callbacks to share common setup or constraints between actions.
 def location_params
      params[:location].permit(:name, :notes, :longitude, :country, :latitude, :query, assets_attributes: [ :asset, :asset_content_type, :asset_file_name, :tempfile, :asset_file_size, :asset_updated_at, :_destroy])
    end
 end

Form View

<%= nested_form_for(@location, :html=> {:multipart => true}) do |f| %>

...

  <%= f.fields_for :assets do |a| %>
    <%= a.file_field :asset %>
    <%= a.link_to_remove "Remove this image" %>
  <% end %>
<%= f.link_to_add "Add an image", :assets %>

...

    <%= f.submit "Submit", :class => "btn btn-success submit_location" %>

<% end %>

Log output

Processing by LocationsController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"n4spoLjq4B3sZSJjqsGFRVjkseOwGgvquAHATBRG1Nk=", "location"=>{"name"=>"York", "notes"=>"", "lat
itude"=>"53.96230079999999", "longitude"=>"-1.0818844", "country"=>"", "assets_attributes"=>{"0"=>{"asset"=>#<ActionDispatch::Http::UploadedFile
:0x007ff739b7bb68 @tempfile=#<Tempfile:/var/folders/sc/gps8hkgj7yg31j81gpnfg9h00000gn/T/RackMultipart20140706-43312-kdpghs>, @original_filename=
"78509.max1024.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"location[assets_attributes][0][asset]\"; filen
ame=\"78509.max1024.jpg\"\r\nContent-Type: image/jpeg\r\n">, "_destroy"=>"false"}}}, "commit"=>"Submit", "id"=>"240"}
  User Load (0.6ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = 1  ORDER BY "users"."id" ASC LIMIT 1
  Location Load (0.4ms)  SELECT  "locations".* FROM "locations"  WHERE "locations"."id" = $1 LIMIT 1  [["id", 240]]
   (0.2ms)  BEGIN
   (0.3ms)  COMMIT
Redirected to http://localhost:3000/locations/240
Completed 302 Found in 9ms (ActiveRecord: 1.6ms)

Upvotes: 2

Views: 2233

Answers (2)

Milind
Milind

Reputation: 5112

try using <%= a.file_field :asset, :multiple=>"true",:name=>"location[assets][asset][]"%> for handling multiple uploads.

Hope it helps

Upvotes: 0

Kirti Thorat
Kirti Thorat

Reputation: 53018

I see couple of problems in your code:

First thing, you need to remove the following line from Location model:

attr_accessor :asset, :assets_attributes

as its making asset and asset_attributes as virtual attributes which is why they are not saved in database. Also, you don't need asset attribute in Location model as its been taken care by Asset model.

Then, update the location_params as suggested by @Pavan:

def location_params
  ## Use `require` method
  params.require(:location).permit(:name, :notes, :longitude, :country, :latitude, :query, assets_attributes: [ :asset, :asset_content_type, :asset_file_name, :tempfile, :asset_file_size, :asset_updated_at, :_destroy])
end

Next, update the create action as below to ensure Locations are unique by name:

def create
  @location = Location.find_by(name: location_params[:name])
  unless @location
    @location = Location.new(location_params)
  end 
  respond_to do |format|
    if @location.save
      format.html { redirect_to @location, notice: ' <borat voice> Great success! </borat voice>' }
      format.json { render :show, status: :created, location: @location }
    else
      format.html { render :new }
      format.json { render json: @location.errors, status: :unprocessable_entity }
    end
  end
end 

Upvotes: 2

Related Questions