. . .

Using has_many with Nested Form in Rails 4

Published: June 14, 2015

On This Page

    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 tech help of your startup/business? Experts from our team will get in touch with you.

        Please do not post jobs/internships inquiries here.