. . .

Using has_many with Nested Form in Rails 4

Published: June 14, 2015

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 %>

 

Don't forget to share this post

    Let's Build Digital Excellence Together


    • Cost Efficient Solutions.
    • Minimal Timelines.
    • Effective Communication.
    • High Quality Standards.
    • Lifetime Support.
    • Transparent Execution.
    • 24/7 Availability.
    • Scalable Teams.

    Join Our 200+ Happy Clients Across Globe


    Free Consultation.

      Do you need help in strategy, innovation or growth of your startup/business? Experts from our team will get in touch with you.

      Please do not post jobs/internships inquiries here.