Search code examples
ruby-on-railsruby-on-rails-4rails-activerecordnested-attributespolymorphic-associations

Rails: Polymorphic assosiation and accepts_nested_attributes


The attachment won't save. What I am missing here? In my application I have a project ,for each project user can upload many assets. The Upload is done by carrier wave.

here are the models

class Project < ActiveRecord::Base

   has_many :assets,:as => :assetable,dependent: :destroy
   accepts_nested_attributes_for :assets, :allow_destroy => true

end


class Asset < ActiveRecord::Base

  belongs_to :project
  belongs_to :user
  belongs_to :assetable, :polymorphic => true
  mount_uploader :attachment, AttachmentUploader #carrierwave
  validates :attachment, presence: true
  validates :project_id, presence: true
end

and these are the actions in my project_controller

  def new
     @project = Project.new
     @asset = @project.assets.build
   end 



  def create
      @project = Project.new(project_params)
      @project.assets.build
      respond_to do |format|
          if @project.save
              format.html { redirect_to @project, notice: 'Project was successfully created.' }
              format.json { render :show, status: :created, location: @project }
          else 
              format.html { render :new }
              format.json { render json: @project.errors, status: :unprocessable_entity }
          end 
end


def project_params
  params.require(:project).permit(:user_id,  :summary, :start_date,assets_attributes: [:id, :project_id, :attachment,:user_id] )
end

this is how the form looks like

   <%= form_for @project,:html => {:multipart => true } do |f| %> 
     <% if @project.errors.any? %>
     <div id="error_explanation">
     </div>
     <% end %>
     <%= f.fields_for :assets do |p| %>
         <div class="field">
             <%= p.label :attachment %><br>
             <%= p.file_field :attachment,name: "assets[attachment][]" %>
          </div>
     <% end %>
     <div class="actions">
         <%= f.submit %>
      </div>
    <% end %>

Solution

  • schema.rb

    create_table "assets", force: true do |t|
        t.string   "assetable_id"
        t.string   "assetable_type"
        t.string   "attachment"
        t.datetime "created_at"
        t.datetime "updated_at"
      end
    
    create_table "projects", force: true do |t|
        t.string   "user_id"
        t.string   "summary"
        t.datetime "created_at"
        t.datetime "updated_at"
      end
    

    projects/_form.html.erb

     <%= form_for @project, :html => {:multipart => true } do |f| %>
     <% if @project.errors.any? %>
        <div id="error_explanation">
          <%= @project.errors.inspect %>
        </div>
     <% end %>
    
     <%= f.label :summary %>
     <%= f.text_field :summary %>
    
     <%= f.fields_for :assets do |p| %>
       <div class="field">
         <%= p.label :attachment %><br>
         <%= p.file_field :attachment %>
    
         <%= p.hidden_field :assetable_id %>
         <%= p.hidden_field :assetable_type %>
        </div>
     <% end %>
    
     <div class="actions">
         <%= f.submit %>
      </div>
    <% end %>
    

    projects_controller.rb

      # GET /projects/new
      def new
        @project = Project.new
        @project.assets.build
      end
    
      # POST /projects
      # POST /projects.json
      def create
        @project = Project.new(project_params)
        respond_to do |format|
          if @project.save
              format.html { redirect_to @project, notice: 'Project was successfully created.' }
              format.json { render :show, status: :created, location: @project }
          else
              format.html { render :new }
              format.json { render json: @project.errors, status: :unprocessable_entity }
          end
        end
      end
    
      private
    
        # Never trust parameters from the scary internet, only allow the white list through.
        def project_params
          params.require(:project).permit(:user_id,  :summary, :start_date, assets_attributes: [:id, :assetable_id, :assetable_type, :attachment, :user_id] )
        end
    

    project.rb

    class Project < ActiveRecord::Base
       has_many :assets, :as => :assetable, dependent: :destroy
       accepts_nested_attributes_for :assets, :allow_destroy => true
    end
    

    asset.rb

    class Asset < ActiveRecord::Base
      belongs_to :project
      belongs_to :assetable, :polymorphic => true
    
      mount_uploader :attachment, AttachmentUploader #carrierwave
    
      validates :attachment, presence: true
      validates :assetable_type, presence: true
    end
    

    Aside

    Since you based this question off a previous question you asked I'll just mention: You only want to use a polymorphic association if you intend instances of your asset class to belong to different types of classes (ie. things other than a project).