This post shows how to use field_for and accepts_nested_attributes_for to created nested form for has_many model

Let suppose the following scenario. We have multiple projects and each project can have multiple screenshots. Following will be the definition of model classes.

class Project < ActiveRecord::Base
   has_many :screenshots
end
class Screenshot < ActiveRecord::Base
  belongs_to :project
end

Now while adding a new project, we want to upload images for that project as well. This requires changes in all three type of files, i.e. Model, View, Controller. First the Project model, we need to specify that this model will accept attributes for its associated model. The following line of code does that

 accepts_nested_attributes_for :screenshots, :reject_if => lambda { |a| a[:image].blank? }, :allow_destroy => true

The reject_if block specifies that image attribute is required for form to be submitted successfully.

Now in the controller we need to initialize the associated model so the new method will be as follows

class ProjectsController &lt; ApplicationController

  def new
    @project = Project.new
    @project.screenshots.new
  end
  
  def project_params
      params.require(:project).permit(:title, :description, :screenshots => [:image])
  end

end

The form view will have fields for the associated model.

<%= form_for @project, :html => {:multipart => true,  :class => "form-horizontal project" } do |f| %>
  <div class="form-group">
    <%= f.label :title, :class => 'col-sm-2 control-label' %>
    <div class="col-sm-10">
      <%= f.text_field :title, :class => 'form-control' %>
    </div>
    <%= error_span(@project[:title]) %>
  </div>
  <div class="form-group">
    <%= f.label :description, :class => 'col-sm-2 control-label' %>
    <div class="col-sm-10">
      <%= f.text_area :description, :class => 'form-control' %>
    </div>
    <%= error_span(@project[:description]) %>
  </div>
  <div class="form-group">
    <%= f.fields_for :screenshots, @project.screenshots do |f_screenshot| %>
      <%= f_screenshot.label :image, :class => 'col-sm-2 control-label' do %>
        Screenshots
      <% end %>
      <div class="col-sm-10">
        <%= f_screenshot.file_field :image, :class => 'form-control' %>
      </div>
    <% end %>
  </div>

  <div class="form-actions">
      <%= f.submit nil, :class => 'btn btn-primary' %>
      <%= link_to t('.cancel', :default => t("helpers.links.cancel")),
                projects_path, :class => 'btn btn-default' %>
  </div>
<% end %>