1

I'm using this example to create multiple image uploads using Carrierwave Rails 4 multiple image or file upload using carrierwave. For some reason if I edit the Post and try to upload a different image it doesn't update.

listings_controller.rb

class ListingsController < ApplicationController
      before_action :set_listing, only: [:show, :edit, :update, :destroy]
      before_filter :authenticate_user!, :except => [:show, :index]

  def index
     @listings = Listing.order('created_at DESC')

     respond_to do |format|
        format.html
        format.json { render json: @listings }
     end
  end

  def show
     @image_attachments = @listing.image_attachments.all
  end

  def new
     @listing = Listing.new
     @listing.user = current_user
     @image_attachment = @listing.image_attachments.build
  end

  def edit
  end

  def create
     @listing = Listing.new(listing_params)
     @listing.created_at = Time.now
     @listing.user = current_user

     respond_to do |format|
        if @listing.save
           params[:image_attachments]['image'].each do |a|
              @image_attachment = @listing.image_attachments.create!(:image => a, :listing_id => @listing.id)
           end
           format.html { redirect_to @listing, notice: 'Post was successfully created.' }
        else
           format.html { render action: 'new' }
           format.json { render json: @listing.errors, status: :unprocessable_entity }
        end
     end
  end

  def update
     respond_to do |format|
        if @listing.update(listing_params)
           flash[:notice] = 'Deal was successfully updated.'
           format.html { redirect_to @listing }
           format.json { head :no_content }
        else
           format.html { render action: 'edit' }
           format.json { render json: @listing.errors, status: :unprocessable_entity }
        end
     end
  end

  def destroy
     @listing.destroy

     respond_to do |format|
        format.html { redirect_to listings_url }
        format.json { head :no_content }
     end
  end

  private
     # Use callbacks to share common setup or constraints between actions.
     def set_listing
        @listing = Listing.friendly.find(params[:id])
     end

     # Never trust parameters from the scary internet, only allow the white list through.
     def listing_params
        params.require(:listing).permit(:condition, :listing_title, :nickname, :listing_size, :listing_price, :user_id, image_attachments_attributes: [:id, :listing_id, :image])
     end


end

listing form

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

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

   <%= f.fields_for :image_attachments do |p| %>
      <div>
         <%= p.label :image %>
         <%= p.file_field :image, :multiple => true, name: "image_attachments[image][]", :class => 'upload' %>
      </div>
   <% end %>

   <div class="actions">
      <%= f.submit 'Submit', :class => 'submitButton' %>
   </div>
<% end %>

listing.rb

   has_many :image_attachments
   accepts_nested_attributes_for :image_attachments

Any help? Thanks.

UPDATE

This is the log ouput when I try to update the image field. "about.png" is the new image I'm trying to upload.

Started PATCH "/listings/nike-air-max-90" for 127.0.0.1 at 2014-07-16 11:40:14 -0400
Processing by ListingsController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"LU1ADy5JqfuX9CMDtcG/dmGgu9nuvplDQrVixfICsS4=", "listing"=>{"listing_title"=>"Nike Air Max 90", "nickname"=>"", "listing_size"=>"9.5", "listing_price"=>"160", "image_attachments_attributes"=>{"0"=>{"id"=>"1"}}}, "image_attachments"=>{"image"=>[#<ActionDispatch::Http::UploadedFile:0x00000109506810 @tempfile=#<Tempfile:/var/folders/vk/x5f3g8n147z_j39_mzkbfq600000gp/T/RackMultipart20140716-1370-63vlgx>, @original_filename="about.png", @content_type="image/png", @headers="Content-Disposition: form-data; name=\"image_attachments[image][]\"; filename=\"about.png\"\r\nContent-Type: image/png\r\n">]}, "commit"=>"Submit", "id"=>"nike-air-max-90"}
  [1m[35mListing Load (0.2ms)[0m  SELECT "listings".* FROM "listings" WHERE "listings"."slug" = 'nike-air-max-90' ORDER BY "listings"."id" ASC LIMIT 1
  [1m[36mUser Load (0.2ms)[0m  [1mSELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1[0m
  [1m[35m (0.1ms)[0m  begin transaction
  [1m[36mImageAttachment Load (0.1ms)[0m  [1mSELECT "image_attachments".* FROM "image_attachments" WHERE "image_attachments"."listing_id" = ? AND "image_attachments"."id" IN (1)[0m  [["listing_id", 2]]
  [1m[35m (0.1ms)[0m  commit transaction
Redirected to http://localhost:3000/listings/nike-air-max-90
Completed 302 Found in 5ms (ActiveRecord: 0.6ms)
Community
  • 1
  • 1
Chris
  • 144
  • 1
  • 13
  • Try adding an `:id` to the params of `listing_params` method like this `params.require(:listing).permit(:id,:condition, :listing_title, :nickname, :listing_size, :listing_price, :user_id, image_attachments_attributes: [:id, :listing_id, :image])` – Pavan Jul 16 '14 at 15:28
  • It still doesn't update. @Pavan – Chris Jul 16 '14 at 15:33
  • Any errors in your log? – Monideep Jul 16 '14 at 15:37
  • For more information, try having `listing_params` simply return `params`, and see if it updates. If it does, then at least we know it's the filter, if not we can look elsewhere. Also, the server log of the update action would be very helpful. – JTG Jul 16 '14 at 15:38
  • I updated my question with the log output. @JTG – Chris Jul 16 '14 at 16:05

1 Answers1

0

Option 1 (Replace all existing attachments with new uploaded ones

In your update action, you are NOT doing what you are doing in create action. Which is this:

params[:image_attachments]['image'].each do |a|
  @image_attachment = @listing.image_attachments.create!(:image => a, :listing_id => @listing.id)
end

You can't expect Rails to do this for you magically because this is not a typical use of accepts_nested_attributes feature. In fact, in your current code, you are not using this feature at all.

If you want to make it work with your current code, you will have to delete all existing image_attachments and create the new ones in the update action, like this:

def update
  respond_to do |format|
    if @listing.update(listing_params)
       if params[:image_attachments] && params[:image_attachments]['image']

         # delete existing image_attachments
         @listing.image_attachments.delete_all

         # create new ones from incoming params
         params[:image_attachments]['image'].each do |a|
           @image_attachment = @listing.image_attachments.create!(:image => a, :listing_id => @listing.id)
         end

       end
       flash[:notice] = 'Deal was successfully updated.'
       format.html { redirect_to @listing }
       format.json { head :no_content }
    else
       format.html { render action: 'edit' }
       format.json { render json: @listing.errors, status: :unprocessable_entity }
    end
  end
end

This will replace all existing images with new ones, if you upload new ones.

Option 2 (Edit them individually)

If you want to be able to update existing attachments, you will have to modify the edit form to allow updating attachment records individually. Or do it via proper use of accepts_nested_attributes feature. Cocoon is one great gem help you incorporate nested attributes in your forms easily.

San
  • 1,954
  • 1
  • 14
  • 18
  • What's the right way to create a nested attribute? From reading tutorials on def new i changed `@image_attachment = @listing.image_attachments.build` to `@listing.image_attachments.build`. I removed `params[:image_attachments]['image'].each do |a|` from def create and changed the form to `<%= f.fields_for :image_attachments do |builder| %>` but it doesn't actually save the images. – Chris Jul 16 '14 at 21:01
  • It won't work with the multiple file upload feature that you are trying to use. so, if you want to do multiple file upload, you will have to stick with option 1. If you want to go with option 2, read up documentation on `cocoon` gem that I mentioned. – San Jul 16 '14 at 21:07