Search code examples
ruby-on-railsruby-on-rails-3polymorphismcarrierwavepolymorphic-associations

Rails upload form with carrierwave and polymorphic associations


I'm trying to wrap my head around using polymorphic associations to make file management less repetitive in a simple rails app. I'm using carrierwave to handle the file uploading. Here's what I have so far:

app/uploaders/file_uploader.rb

class FileUploader < CarrierWave::Uploader::Base
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end
end

app/models/attachment.rb

class Attachment < ActiveRecord::Base
  mount_uploader :file, FileUploader
  belongs_to :attachable, polymorphic: true
end

app/models/photo.rb

class Photo < ActiveRecord::Base
  attr_accessible :caption, :attachment
  has_one :attachment, as: :attachable
end

I can handle this fine in the rails console:

$ rails console
> photo = Photo.new
> attachment = Attachment.new
> attachment.file = File.open('tmp/demo.png')
> photo.attachment = attachment
> photo.save
> photo.attachment
=> #<Attachment id: 3, file: "demo.png", attachable_id: 5, attachable_type: "Photo", created_at: "2013-04-13 16:56:31", updated_at: "2013-04-13 16:56:31">

So my problem is really in the photos controller:

ActiveRecord::AssociationTypeMismatch in PhotosController#create
Attachment(#70310274945400) expected, got ActionDispatch::Http::UploadedFile(#70310271741380)`

Any help on this is hugely appreciated. I might not have the greatest grasp of polymorphic associations.

UPDATE

Taking @manoj's suggestion, I edited the photo form to nest the attachment:

<%= f.fields_for :attachment do |attachment_f| %>
    <%= attachment_f.file_field :file %>
<% end %>

I'm now getting this error when I attempt to submit the form:

ActiveRecord::AssociationTypeMismatch (Attachment(#70135925415240) expected, got ActiveSupport::HashWithIndifferentAccess(#70135923190420)):
app/controllers/photos_controller.rb:43:in 'new'
app/controllers/photos_controller.rb:43:in 'create'

Solution

  • The post params should be something like this

    params => { "photo" => 
                   { :attachment_attribute => 
                      {:file => ActionDispatch::Http::UploadedFile}
                   }
              }
    

    but your post param most probably is

    params => { "photo" => { :attachment => ActionDispatch::Http::UploadedFile}}
    

    Your view should contain fields_for to handle the nesting in models. You have to create a file field for the attribute "file" of the Attachment model

    <%=form_for @photo do |photo_f|%>
      ....
      <%=photo_f.fields_for :attachment do |attachment_f|%>
         <%= attachment_f.file_field :file%>
      <%end%>
      ....
    <%end%>
    

    UPDATE

    <%=photo_f.fields_for :attachment_attributes do |attachment_f|%>
    

    And in the photo model add these,

    accepts_nested_attributes_for :attachment
    attr_accessible ..., :attachment_attributes