Topic: Multiple file uploads w/ paperclip and nested attributes

Hi all Rubsters

First off – this is my first post so go easy on me.

I have been using rails for the sum total 10 days. It kicks a*** - especially mine.
Delving into Paperclip, I have been easily able to upload a photo, create and display different crops, following the tutorial from Pat Shaughnessy (http://patshaughnessy.net/2009/4/30/pap … sample-app); however, when I try to use another model for an album of photos, all hell breaks loose.

I kick off with:

script/generate scaffold Album name:string
script/generate model Photo name:string album_id:integer
script/generate paperclip photo data
rake db:migrate (of course)
script/server (naturally)

#album.rb
class Album < ActiveRecord::Base
  has_many :photos
  accepts_nested_attributes_for :photos, :allow_destroy => true
end

#photo.rb
require 'paperclip'

class Photo < ActiveRecord::Base
  belongs_to :album, :class_name => "Album", :foreign_key => "album_id"
  has_attached_file :data,
                     :styles => {
                       :thumb => "75x75>",
                       :small => "200x200>"
                     }
end

but I think the real problem lays in albums_controller.rb:

#albums_controller.rb
def index
    @albums = Album.all
end

def show
    @album = Album.find(params[:id])
end

def new
    @album = Album.new
end

def edit
    @album = Album.find(params[:id])
end

def create
    @album = Album.create(params[:album])

    respond_to do |format|
      if @album.save
      #blah blah
     end
end

def update
    @album = Album.find(params[:id])

    respond_to do |format|
      if @album.update_attributes(params[:album])
        flash[:notice] = 'Album was successfully updated.
        #blah blah
      end
    end
end

def destroy
    @album = Album.find(params[:id])
    @album.destroy
end

#application_helper.rb
module ApplicationHelper
  def setup_album(album)
      returning(album) do |a|
        a.photos.build if a.photos.empty?
      end
    end
end

#new.html.erb
<h1>New album</h1>

<% form_for(setup_album(@album), :html => { :multipart => true }) do |f| %>
    <%= f.error_messages %>

    <p><%= f.label :name %><br /><%= f.text_field :name %></p>

    <% f.fields_for @album.photos.build do |p| %>
            <h2>Photo</h2>
            <p><%= p.label :name %><br /><%= p.text_field :name %></p>
              <p><%= f.label :data %><br /><%= f.file_field :data %></p>
    <% end %>
       
    <p><%= f.submit 'Create' %></p>
<% end %>

<%= link_to 'Back', albums_path %>

http://localhost:3000/albums/new
displays the right fields, but on submit I get:

ActiveRecord::UnknownAttributeError in AlbumsController#create
unknown attribute: photo

I would be (more than) grateful for any assistance

Thanks to all,
Adam

Re: Multiple file uploads w/ paperclip and nested attributes

As a test I cut and pasted everything from this tutorial - http://www.webyfi.com/?p=222 - and still no cigar.
As mentioned above I have already followed Pat Shaugnessy’s tutorial to get paperclip running, so I know that I have paperclip and dependencies running.

I am truly baffled
Anybody?
Don't make me beg!
Please don't make me beg – oops! there I go :-)

Adam

Re: Multiple file uploads w/ paperclip and nested attributes

How did you get on with this Abuwabu?

Re: Multiple file uploads w/ paperclip and nested attributes

Solved thanks think-in. 5 months into my ror learning curve and getting pretty good now ;-)

Re: Multiple file uploads w/ paperclip and nested attributes

and how did you fixed it?

Re: Multiple file uploads w/ paperclip and nested attributes

just to make this post complete (abuwabu, people have taken the time to help you, the least you could do is return that help to the community and explain what the fix was, to do otherwise is completely selfish!  rant over...) I found (after a lot of searching) a solution:

just have to add the following Gist to your application_helper.rb

https://gist.github.com/403687

  def link_to_add_fields(name, f, association)
    new_object = f.object.class.reflect_on_association(association).klass.new
    fields = f.fields_for(association, [new_object], :child_index => "new_#{association}") do |builder|
      render(association.to_s.singularize + "_fields", :f => builder)
    end
    link_to_function(name, h("add_fields(this, \"#{association}\", \"#{escape_javascript(fields)}\")"))
  end

That worked for me!  (Although I have no idea what it does, and what it might have broken...)

Last edited by horseshoe7 (2011-02-08 06:19:01)

Re: Multiple file uploads w/ paperclip and nested attributes

First off horseshoe — no-one did take the time to help me. It was just me and the tumbleweed blowing through this thread, so I figured it out on my own.
Secondly, it was in 2009. And it was only when I got a random email this morning from railsforum that I came back.
However, I am happy to help anybody that asks nicely, so here is the solution for rails 3:

Make sure you have the paperclip gem installed:

#in Gemfile
gem 'paperclip'

You need one model to paperclip the upload to, let's call it photo; and a model to contain all those photos, we'll call it album.

#in terminal
bundle
rails g scaffold photo title:string album_id:integer
rails g scaffold album name:string 
rails g paperclip photo upload
rake db:migrate


#models/album.rb
class Album < ActiveRecord::Base
  has_many :photos, :dependent => :destroy
  accepts_nested_attributes_for :photos, :allow_destroy => true
end

#models/photo.rb
require 'paperclip' #don't forget this
class Photo < ActiveRecord::Base

  belongs_to :album
  has_attached_file :upload,
                    :url => "/images/:id/:style/:basename.:extension",
                    :path => ":rails_root/public/images/:id/:style/:basename.:extension",
                    :styles => {
                      :thumb => "75x75>",
                      :small => "200x200>"
                    }
  #add in any validations you may want
end


#views/albums/index.html
<h1>Listing albums</h1>

<table>
  <tr>
    <th>Name</th>
    <th>Photo Title</th>
    <th>Photos</th>
  </tr>

<% @albums.each do |album| %>
  <tr>
    <td><%=h album.name %></td>
  <% album.photos.each do |photo| %>
    <td><%=h photo.title %></td>
    <td><%= image_tag photo.upload.url(:thumb) %></td>
  <% end %>
    <td><%= link_to 'Show', album %></td>
    <td><%= link_to 'Edit', edit_album_path(album) %></td>
    <td><%= link_to 'Destroy', album, :confirm => 'Are you sure?', :method => :delete %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to 'New album', new_album_path %>


#views/albums/_form.html.erb
<% form_for(@album, :html => { :multipart => true }) do |f| %>
  
  <p>
    <%= f.label :name %>
    <%= f.text_field :name %>
  </p>
  
  <h2>Photo(s)</h2>
    <div id="photos">
      <% if @album.new_record? %>
         <%= render :partial => 'photo', :locals => { :form => f, :photo => @album.photos.build } %>
      <% else %>
        <% @album.photos.each do |photo| %>
          <%= image_tag photo.upload.url(:small) %>
        <% end %>
      <% end %>
  </div>
  <%= add_object_link("New Photo", f, @album.photos.build, "photo", "#photos") %>
  
  <p><%= f.submit %></p>
<% end %>


#views/albums/show.html.erb
<p>
  <b>Name:</b>
  <%=h @album.name %><br />
  
  <% @album.photos.each do |photo| %>
    <h3><%=h photo.title %></h3>
    <%= image_tag photo.upload.url(:small) %>
  <% end %>
</p>


<%= link_to 'Edit', edit_album_path(@album) %> |
<%= link_to 'Back', albums_path %>


#in layouts/application.html.erb you need to include jquery (and make sure jquery-1.3.2.min.js is in public/javascripts)
<%= javascript_include_tag :defaults, 'jquery-1.3.2.min' %>
<script>
   jQuery.noConflict();
</script>


#helpers/albums_helper.rb
module AlbumsHelper
  def add_object_link(name, form, object, partial, where)
      html = render(:partial => partial, :locals => { :form => form}, :object => object)
      link_to_function name, %{
        var new_object_id = new Date().getTime() ;
        var html = jQuery(#{js html}.replace(/index_to_replace_with_js/g, new_object_id)).hide();
        html.appendTo(jQuery("#{where}")).slideDown('slow');
      }
    end

    def js(data)
      if data.respond_to? :to_json
        data.to_json
      else
        data.inspect.to_json
      end
    end
end


#public/javascripts/application.js
function remove_field(element, item) {
  element.up(item).remove();
}


#routes.rb
resources :albums do
  resources :photos
end

root :to => "albums#index"

Hope that helps someone...

Last edited by abuwabu (2011-02-10 05:47:24)

Re: Multiple file uploads w/ paperclip and nested attributes

@abuwabu your post helped me quite a bit!

just got some troubles about what to write in the photo partial.

Re: Multiple file uploads w/ paperclip and nested attributes

Ah, yes, sorry about the glaring omission:

#views/albums/_photo.html.erb
<div class="photo">
  <%= form.fields_for :photos, photo, :child_index => (photo.new_record? ? "index_to_replace_with_js" : nil) do |photo_form| %>
    <p>
      <%= photo_form.label :title %>
      <%= photo_form.text_field :title %>
      <%= photo_form.file_field :upload %>
      <%= link_to_function "delete", "remove_field($(this), ('.photo'))" %>
    </p>
  <% end %>
</div>

Last edited by abuwabu (2011-02-10 05:44:06)

Re: Multiple file uploads w/ paperclip and nested attributes

works like charm now smile thanks again!

Re: Multiple file uploads w/ paperclip and nested attributes

it is easy, just add this to your gem file:

gem 'paperclip', :git => 'http://github.com/thoughtbot/paperclip.git'

Last edited by imon (2011-04-28 04:13:45)