mongodb/mongoid

View on GitHub
docs/reference/nested-attributes.txt

Summary

Maintainability
Test Coverage
.. _nested-attributes:

*****************
Nested Attributes
*****************

.. default-domain:: mongodb

.. contents:: On this page
   :local:
   :backlinks: none
   :depth: 2
   :class: singlecol

Nested attributes provide a mechanism for updating documents and their
associations in a single operation by nesting attributes in a single
parameters hash. This is useful when wanting to edit multiple documents
within a single web form.

Behavior
========

Nested attributes can be enabled for any association, embedded or referenced.
To enable this for an association, simply provide the association name to the
``accepts_nested_attributes_for`` macro.

.. code-block:: ruby

   class Band
     include Mongoid::Document
     embeds_many :albums
     belongs_to :producer
     accepts_nested_attributes_for :albums, :producer
   end

Note that when you add nested attributes functionality to a referenced
association, Mongoid will automatically enable autosave for that association.

When an association gains nested attributes behavior, an additional method is
added to the base model, which should be used to update the attributes with
the new functionality. This method is the association name plus ``_attributes=``.
You can use this method directly, or more commonly the name of the method can
be an attribute in the updates for the base class, in which case
Mongoid will call the appropriate setter under the covers.

.. code-block:: ruby

   band = Band.first
   band.producer_attributes = { name: "Flood" }
   band.attributes = { producer_attributes: { name: "Flood" }}

Note that this will work with any attribute based setter method in Mongoid,
including ``update``, ``update_attributes`` and ``attributes=``, as well as
``create`` (and all of their corresponding bang methods). For example, creating
a new person with associated address records can be done in a single
statement, like this:

.. code-block:: ruby

   person = Person.create(
      name: 'John Schmidt',
      addresses_attributes: [
         { type: 'home', street: '1234 Street Ave.', city: 'Somewhere' },
         { type: 'work', street: 'Parkway Blvd.', city: 'Elsewehre' },
      ])


Creating Records
----------------

You can create new nested records via nested attributes by omitting
an ``_id`` field:

.. code-block:: ruby

   person = Person.first
   person.update(addresses_attributes: [
      { type: 'prior', street: '221B Baker St', city: 'London' } ])

This will append the new record to the existing set; existing records will
not be changed.


Updating Records
----------------

If you specify an ``_id`` field for any of the nested records, the attributes
will be used to update the record with that id:

.. code-block:: ruby

   person = Person.first
   address = person.addresses.first
   person.update(addresses_attributes: [
      { _id: address._id, city: 'Lisbon' } ])

Note that if there is no record with that id, a ``Mongoid::Errors::DocumentNotFound``
exception will be raised.


Destroying Records
------------------

You can also destroy records this way, by specifying a special
``_destroy`` attribute. In order to use this, you must have passed
``allow_destroy: true`` with the ``accepts_nested_attributes_for``
declaration:

.. code-block:: ruby

   class Person
      # ...

      accepts_nested_attributes_for :addresses, allow_destroy: true
   end

   person = Person.first
   address = person.addresses.first
   person.update(addresses_attributes: [
      { _id: address._id, _destroy: true } ])

Note that, as with updates, if there is no record with that id,
a ``Mongoid::Errors::DocumentNotFound`` exception will be raised.


Combining Operations
--------------------

Nested attributes allow you to combine all of these operations in
a single statement! Here's an example that creates an address,
updates another address, and destroys yet another address, all in
a single command:

.. code-block:: ruby

   person = Person.first
   person.update(addresses_attributes: [
      { type: 'alt', street: '1234 Somewhere St.', city: 'Cititon' },
      { _id: an_address_id, city: 'Changed City' },
      { _id: another_id, _destroy: true } ])