ravip0711
ravip0711

Reputation: 511

How to create multiple records with a nested form using check boxes

I have a Pizza model and Topping model with a PizzaTopping join table. Pizza has many toppings and topping belongs to pizza. I'm a novice with Rails.

My problem is trying to understand how to created a nested form that will add multiple records into my PizzaTopping join table. I also need the toppings to show up in check box form.

<div class="form-horizontal">
  <%= form_for(@pizza) do |f| %>

Confusion #1:

To my understand this is to show a full model in check boxes, which works, but I'm confused on how the controller accepts this and creates the records in the join table of the toppings associated with the pizza. I want separate records(and not an attribute that is an array of topping ids):

PizzaTopping.create(id: 1, pizza_id: 1, topping_id: 1)
PizzaTopping.create(id: 2, pizza_id: 1, topping_id: 2)
PizzaTopping.create(id: 3, pizza_id: 1, topping_id: 3)

..

  <div class="form-group">
    <%= f.collection_check_boxes(:topping_ids, Topping.all, :id, :name) do |b| %>
      <%= b.check_box %>
      <%= b.label %>
    <% end %>
  </div>

or Confusion # 2:

This is a nested form but how do I get the toppings in check boxes from the Topping model and same as above, how do I code this in my controller to add records in the join table.

<div class="form-group">
  <%= f.fields_for :toppings do |builder| %>
    <%= builder.check_box %> // confused what I would even do next

  <% end %>
</div>

...

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

Upvotes: 1

Views: 548

Answers (2)

max
max

Reputation: 102368

First lets setup a indirect many to many relationship though the pizza_toppings table.

class Pizza < ActiveRecord::Base
  has_many :pizza_toppings
  has_many :toppings, through: :pizza_toppings
end

class Topping < ActiveRecord::Base
  has_many :pizza_toppings
  has_many :pizzas, through: :pizza_toppings
end

class PizzaTopping < ActiveRecord::Base
  belongs_to :pizza
  belongs_to :topping
end

What this accomplishes is that it lets you associate any number of pizzas with any number of toppings and ActiveRecord will handle joining for you:

@pizza = Pizza.find_by(name: 'Vesuvio') 
@pizza.toppings 
# => Topping( name: cheese ) ...
@pizza.toppings << Topping.find_by(name: 'Ham')
# inserts a record into the pizza_toppings table
# you can also do the inverse
@topping = Topping.find_by(name: 'Anchovies') 
@topping.pizzas
# => Pizza( name: 'Napoli' )

To setup one to any or many to many relations via a checkbox you can use the collection_check_boxes helper.

<% form_for(@pizza) do |f| %>
  <% f.collection_check_boxes(:topping_ids, Topping.all, :id, :name) %>
<% end %>

When you give a model a has_many association it gets a _ids setter which takes an array of ids and adds / removes associations, in this case ActiveRecord is also smart enough to know that it should setup the association through the join table when you use the through option.

The checkboxes generated by collection_check_boxes give you just that - an array in the params containing the ids of the selected toppings.

Note that you don't need to use fields_for here unless you intend to let users create pizzas and toppings on the same page. Also make sure you whitelist the topping_ids param.

def pizza_params
  params.require(:pizza).permit(:name, topping_ids: [])
end

And now you got me all hungry.

Upvotes: 4

John Hayes-Reed
John Hayes-Reed

Reputation: 1438

First things first, if you are using a join table then you need to organise your relations differently, it makes no sense to use a join table with a belongs_to relationship, what you want to do is organise your relations so that a Pizza has_many :toppings, and a Topping has_many :pizzas and use the through: :pizza_toppings key.

Now onto your first confusion, If you are using checkboxes the most you can hope to receive is the value of that checkbox, most likely the ids, this is then down to you to instantiate the records once you have the array of ids, perhaps something along the lines of selected_toppings = Topping.where(id: params[:topping_ids]) which will instantiate a collection of the toppings that were selected. Also if you have your relations set up properly there is no need to explicitly create PizzaTopping records, this is a join table, and in my opinion I prefer to handle everything using the relations, something along the lines of pizza.toppings << selected_toppings should do the trick to set it all up.

Confusion two: I dont think what you are looking for is a nested form, because you are not actually creating toppings, you just want a list of the toppings to relate to a pizza. Nested forms are when you want to create/edit and then write the attributes of a relation, your toppings are already preset, so just simply make checkboxes in the form normally with the values of your toppings ids.

Hope this helps!

Upvotes: 0

Related Questions