1

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>
2
  • So you want to create the pizza and toppings in the same form? Commented Jul 29, 2016 at 0:25
  • I have a separate model for the toppings, where you can add toppings and remove toppings. For this, I just want to be able to create a pizza, create a form that lists in a check box format of all the toppings from the Topping model. Then be able to store the pizza with its toppings Commented Jul 29, 2016 at 1:08

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you sir! It's a coding challenge for a job, and I was trying to follow their models in the repository (which does not have the "through" relationship) that they gave me. I was trying to do the challenge to bypass it but theres no other way besides creating that "through" relationship. The challenge is open ended so we'll see how receptive they are to it. =)
IMHO - using a join table and the setting up the relationship with through: is the correct way to do M2M relations in ActiveRecord. There is a pretty good chance that they where deliberately doing it wrong.
0

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!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.