ruby 57 lines · 3 tabs

Transactionally Create Parent + Children with accepts_nested_attributes_for

Shared by codesnips Jan 2026
3 tabs
class Order < ApplicationRecord
  has_many :line_items, dependent: :destroy, inverse_of: :order

  accepts_nested_attributes_for :line_items,
    reject_if: :all_blank,
    allow_destroy: true

  validates :customer_name, presence: true
  validates :line_items, presence: true

  before_validation :recalculate_total

  private

  def recalculate_total
    self.total_cents = line_items
      .reject(&:marked_for_destruction?)
      .sum { |item| item.unit_price_cents.to_i * item.quantity.to_i }
  end
end
3 files · ruby Explain with highlit

This snippet shows how Rails builds a parent record and its children in a single atomic operation using accepts_nested_attributes_for. The core idea is that one call to save on the parent wraps the entire graph — the Order and all of its LineItem rows — in a single database transaction. If any child fails validation, the parent is not persisted and no partial data is written, which is exactly the guarantee needed when a parent is meaningless without its children.

In Order model, accepts_nested_attributes_for :line_items enables the parent to accept a nested hash or array of child attributes and translate them into associated records. The reject_if proc drops blank rows so empty form fields do not create junk line items, and allow_destroy: true lets an existing child be removed by passing _destroy: true. The validates :line_items, presence: true rule enforces that an order must have at least one item; because nested saves share the parent's transaction, this validation failure rolls the whole thing back. The before_validation callback normalizes derived data before the child records are validated, keeping the parent's total consistent with its items.

LineItem model is an ordinary child that belongs_to :order and validates its own fields. Its validations run as part of the parent's valid? check, and any errors bubble up into order.errors so the form can display them.

OrdersController ties it together. order_params uses strong parameters with a nested line_items_attributes key — note the array-of-permitted-keys form including :id and :_destroy, which is what makes updates and deletions work. The create action simply calls @order.save; there is no manual transaction block because ActiveRecord already opens one. When the save fails, the same @order object carries validation errors for re-rendering the form.

The main trade-off is that autosaved associations run all child callbacks and validations inside one transaction, which can be heavy for very large collections and can hide which specific child failed. It is also easy to forget to permit :id, causing Rails to create duplicates instead of updating. For bounded parent/child graphs like orders and line items, though, this pattern gives clean forms and strong data-integrity guarantees with very little code.


Related snips

Share this code

Here's the card — post it anywhere.

Transactionally Create Parent + Children with accepts_nested_attributes_for — share card
Link copied