Saving nested models in Rails

Very often, I would say almost always, you have nested models in a Web application.

In  Rails, you (most often) specify it using has_many and belongs_to in the respective models.

In my example, I will take the trusty old Order and OrderItem examples.


class Order < ApplicationRecord
  has_many :order_items, dependent: :destroy
  accept_nested_attributes_for :order_items, allow_destroy: true

The bold text means that when saving an Order, it’s possible to also save OrderItems. Rails has all the information to make this possible. The allow_destroy option means that an extra attribute _destroy is added to each OrderItem, and if set in the UI Rails will delete that item and remove it from the Order.

class OrderItem < ApplicationRecord
  belongs_to :order

“belongs_to” add for example a method “order” to the OrderItem so that you easily can refer to the order it belongs to. For example like this:


That way, you won’t have to look up the order using the order_id you have in OrderItem.

The alternative would be to look up the Order manually using the FK order_id.

order = Order.find(order_id)


The controller should stay fairly unchanged. But since you specified that Order now accepts nested parameters for a number of OrderItems, you must permit these parameters.

def order_params
      order_items_attributes: [

Note that is not “permit”


The View is perhaps the trickiest part. But follow this convention.

Start with a form_with statement.

<%= form_with model: [current_user, @order], :local => true do |frm| %>
    <%= frm.fields_for :order_items do |ff| %>

<!-- NOTE that "object" points to the order_item we are processing 
     could be handy if you need to do some processing --> 
          <td><%= %></td>

          <!-- Here we deal with OrderItem fields -->
          <td><%= ff.text_field :product_id %></td>
          <td><%= ff.text_field :qty %></td>
    <% end %>

  <%= frm.submit "Save" %>
<% end %>

Note a couple of things:

[current_user, @order]

since the order is nested within the current user (I’m using devise)

:local => true

otherwise it will use Ajax/JS


can optionally be used to access the OrderItem within the fields_for

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.