Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving multiple records with a single form in Rails 4

I have a model called Family, which belongs_to :user I want to enable the user to add multiple family members in a single form, which is in /views/families/new.html.erb

  • So the user sees a form for creating 3 family members for one user_id
  • Enters data into 3 identical forms, and hits submit
  • The data from 3 individual forms should get saved as 3 different records in the db on submitting just once
  • Now, when I submit the form, all values in the db are saved as NULL.
  • What am I doing wrong? Should I not use strong params?

EDIT: Sorry, I should've provided more detailed code earlier after slight modification in code

# config/routes.rb
resources :families
#app/models/user.rb 
class User < ActiveRecord::Base
  has_many :families
end
#app/models/family.rb
class Family < ActiveRecord::Base
  belongs_to :user
end
#app/controllers/families_controller.rb
class FamiliesController < ActiveRecord::Controller
  def new
  end
  
  def create
    @family = Family.new(family_params)
    a = params[:family][:"0"]
    @family.user_id = current_user.id
    if @family.save
      flash[:notice] = params[:family][:"0"]
      redirect_to dummy_path
    else
      flash[:notice] = "didnt happen"
    end
  end
  
  private
  
  def family_params
    params.require(:family).permit(:all)
  end
end
#app/views/families/_form.html.erb
<%= form_tag(controller: "families") do %>
  <% i = 0 %>
  <% 3.times do %>
    <br>
    <h2>Family Member # <%= i+1 %> : </h2>
    <br>
    <%= fields_for("family[#{i}]") do |f| %>
        <%= f.text_field :name, class: "form-control" , placeholder: "Name"%> <br>
        <%= f.text_field :relationship, class: "form-control" , placeholder: "Relationship" %> <br>
        <%= f.date_field :dob, class: "form-control" %> <br>
        <%= f.text_field :blood_group, class: "form-control", placeholder: "Blood group" %> <br>
        <%= f.text_field :email, class: "form-control", placeholder: "Email" %> <br>
        <%= f.number_field :phone, class: "form-control", placeholder: "Phone number" %> <br>
        <%= f.text_area :hobbies, class: "form-control", placeholder: "Hobbies" %> <br>
        <% i=i+1 %>
    <% end %>
    <hr>
  <% end %><br>
  <%= submit_tag "Submit", class: "btn btn-primary" %> <br>
<% end %>
like image 703
Geekback Avatar asked May 15 '14 08:05

Geekback


3 Answers

You want to use nested forms. You should be able to copy and paste the following code to get going:

app/models/family.rb

class Family < ActiveRecord::Base
  has_many :users

  accepts_nested_attributes_for :users
end

app/models/user.rb

class User < ActiveRecord::Base
  belongs_to :family
end

app/views/families/new.html.erb

<%= form_for(@family) do |f| %>
  <%= f.fields_for :users do |user| %>
    <p>
    <%= user.text_field :name %><br>
    <%= user.text_field :email %>
    </p>
  <% end %>

  <%= f.submit %>
<% end %>

app/controllers/families_controller.rb

  [...]

  def new
    @family = Family.new
    3.times { @family.users.build }
  end

  [...]

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_family
      @family = Family.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def family_params
      params.require(:family).permit(:name, users_attributes: [ :name, :email ])
    end
like image 175
wintermeyer Avatar answered Nov 06 '22 16:11

wintermeyer


The way that i always do this is to make a new action update_multiple. This expects params with the following structure (using your Family example, and updating two records, with ids 123 and 456)

params = {:families => {123 => {:name => "foo", :address => "bar"}, 456 => {:name => "baz", :address => "qux"} }}

and the action works like so:

@families = []
params[:families].each do |id, attributes|
  if family = Family.find_by_id(id)
    family.update_attributes(attributes)
    @families << family
  end
end

In order to set up the params with the required structure, your form would be set up like so:

<% @families.each do |family| %>
  <div class="family">
    <label>Name: <%= text_field_tag "families[#{family_id}][name]", family.name %></label>
    <label>Address: <%= text_field_tag "families[#{family_id}][address]", family.address %></label>
  </div>
<% end %>

You could, instead of making a new action, change the structure of your update action to do the standard code if it gets params[:family] and the above code if it gets params[:families].

like image 45
Max Williams Avatar answered Nov 06 '22 14:11

Max Williams


You need to tweak your code like this.Assuming you have the attribute name in users table.

In your User model

class User < ActiveRecord::Base
  has_many :families, :dependent => :destroy 
  accepts_nested_attributes_for :families, :allow_destroy => true
end

In your users_controller

def new
  @user = User.new
  @family = 3.times { @user.families.build } #here
end

Your strong parametrs should look like this

def user_params 
  params.require(:user).permit(:name, families_attributes: [:name,:email]) 
end

# users/new.html.erb
<%= form_for(@user) do |f| %>
  <div class="field">
    <%= f.label :name %>
    <%= f.text_field :name %>
  </div>
  <%= f.fields_for :familes do |builder| %>
    <%= render 'family_fields', :f => builder %>
  <% end %>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

# users/_family_fields.html.erb
<div>
  <%= f.label :name, "Name" %>        
  <%= f.text_field :name %>
  <br>
  <%= f.label :email, "Email" %>
  <%= f.text_field :email %>
</div>
like image 1
Pavan Avatar answered Nov 06 '22 15:11

Pavan