1

I followed this tutorial trying to integrate Paperclip into my Rails 3 application.

However, my case looks a bit different from what is described in this tutorial.

In my case, User models are already exist in the database, and I want to upload file(s) and associate them with the uploader.

Here are the relevant parts of my code:

class User < ActiveRecord::Base
  has_many :assets, :foreign_key => "uploader_id"
end

class Asset < ActiveRecord::Base
  belongs_to :uploader, :class_name => "User"  
  has_attached_file :asset, :styles => { :thumb => "100x100#" }
end

The main difference between my case and the tutorial is that the upload input field is not inside User's form:

# views/lounge/index.html.erb
<%= form_tag('/lounge/upload', :multipart => true) do %>
  <input id="uploader_id" name="uploader_id" type="hidden" />
  <%= file_field_tag "assets[]", :multiple => true %>
<% end %>

The value of the hidden uploader_id input field is controlled by Javascript.

When the form is submitted the upload method is called:

class LoungeController < ApplicationController
  def upload
    uploader = User.find(params[:uploader_id])
    # ??
  end
end

What should I do with params[:assets] in order to save the uploaded files to the filesystem and create the corresponding Asset models in the database ?

Misha Moroshko
  • 166,356
  • 226
  • 505
  • 746

1 Answers1

1

To save the files, you'll need create the Asset models, and also assign the attached_file to those models (which you've also called asset, which could get confusing). In the simple case, this would just look like:

user = User.find(id)
asset = user.assets.new
asset.asset = params[:asset]
asset.save

To do multiple files, simply do a quick loop:

user = User.find(id)
params[:assets].each do |asset|
  asset = user.assets.new
  asset.asset = asset
  asset.save
end

However, you're doing a number of non-standard things here, and making life harder for yourself than it needs to be. Instead of using file_field_tag, you should be using the file_field helper with a Asset instantiated in memory. Eg, if you followed the tutorial you linked to, you'd have something like:

<% form_for @user do |f|
  <% f.fields_for :assets do |asset| %>
    <%= asset.file_field :asset %>
  <% end %>
<% end %>

You'd also need to tell your User model that it's okay to accept child asset models when saving:

def User
  accepts_nested_attributes_for :assets
end

Then in your controller action, you'd just build a few new assets in memory so that the loop in the fields_for works:

def upload
  @user = User.find(id)
  5.times do { @user.assets.build }
end

Hope this makes sense. Keep going - you'll get there.

Frankie Roberto
  • 1,349
  • 9
  • 9
  • Thanks a lot for your answer! It really helped me a lot! I can't implement this with the user's form (like suggested in the tutorial) because I don't know in advance who is the user that is going to upload the file. I know, it sounds strange, but this is the case :) A couple of questions: **(1)** In case `N` files are uploaded, is it possible to save them at once rather than calling `N` times to `save` ? **(2)** (Stupid question) Could you elaborate on what `user.assets.new` does ? `user.assets` is an `Array`. So you call `new` on `Array` ?!? By the way, it works like a charm :) – Misha Moroshko Jun 22 '11 at 12:57
  • 1) You might be able to omit the asset.save line and instead call user.save at the end - I'm not sure without checking. Try it and see! – Frankie Roberto Jun 22 '11 at 15:44
  • 2) user.assets isn't an array, it's an method created by the has_many association which returns an array. user.assets.new is a different method which return a new Asset object which has the user_id set. There are other methods too: user.assets<<(asset) assigns a new object, user.assets.size returns a count of the number of associated objects, and so on. See http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many for a full list. – Frankie Roberto Jun 22 '11 at 15:50
  • PS I'm curious - why are you setting the user_id as a hidden field via javascript? That sounds like a big security risk... – Frankie Roberto Jun 22 '11 at 15:51
  • Thanks for all! I couldn't make it work with only one `save`. Regarding the hidden field: In my application users may log in as group (e.g. if they share the same computer and don't want to log-in / log-out every time they want to upload a file / write a post). So via the application interface they choose who they are (and this changes the hidden field value). Of course, I check that the value of the hidden field belongs to the logged-in group. – Misha Moroshko Jun 25 '11 at 11:14