Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions source/data-modeling.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Model Your Data

Documents </data-modeling/documents>
Inheritance </data-modeling/inheritance>
Nested Attributes </data-modeling/nested-attributes>

In this section, you can learn how to model data in {+odm+}.

Expand All @@ -24,3 +25,6 @@ In this section, you can learn how to model data in {+odm+}.

- :ref:`mongoid-modeling-inheritance`: Learn how to implement
inheritance in your model classes.

- :ref:`mongoid-modeling-nested-attr`: Learn how to modify documents and
their associations in a single operation.
191 changes: 191 additions & 0 deletions source/data-modeling/nested-attributes.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
.. _mongoid-modeling-nested-attr:

=================
Nested Attributes
=================

.. facet::
:name: genre
:values: reference

.. meta::
:keywords: ruby framework, odm, embeddings, code example, queries

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

Overview
--------

In this guide, you can learn how to define **nested attributes** on
models to enable data operations on documents and their associations.
After you define a nested attribute, you can specify updates to
top-level and associated documents in a single parameter hash. This might be
useful if your application requires editing multiple documents within a single
form.

Behavior
--------

You can enable nested attributes for any association, embedded or
referenced. To add a nested attribute for an association, provide the
association name to the ``accepts_nested_attributes_for`` macro when
defining a model class.

The following code defines embedded associations on the ``Band`` model
class and includes the ``accepts_nested_attributes_for`` macro:

.. literalinclude:: /includes/data-modeling/nested_attr.rb
:start-after: start-simple-nested
:end-before: end-simple-nested
:language: ruby
:emphasize-lines: 5
:dedent:

.. note:: Autosave Enabled

When you add nested attribute functionality to a referenced
association, {+odm+} automatically enables autosave for that
association.

When you enable nested attributes behavior on an association, {+odm+}
adds a special method to the base model. You can use this method to
update the attributes by using this added functionality.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

S: I think you can delete "by using this added functionality"; it seems repetitive after "You can use this method"

Suggested change
update the attributes by using this added functionality.
update the attributes.


The method name is the association name suffixed with ``_attributes``. For
example, the setter method to update the ``producers`` association is
``producer_attributes``.

You can use this method directly, or you can use the name of the method
as an attribute in the updates for the top-level class. In this case,
{+odm+} calls the appropriate setter method internally.

The following code retrieves an instance of ``Band``, then uses the
nested attribute update method ``producer_attributes`` to set a value
for the association document:

.. literalinclude:: /includes/data-modeling/nested_attr.rb
:start-after: start-use-method
:end-before: end-use-method
:language: ruby
:emphasize-lines: 4
:dedent:

There are multiple ways to update a nested attribute:

- Use the ``<association name>_attributes`` setter method.
- Use the ``attributes`` setter method and specify ``<association
name>_attributes`` in the value to update the associations.
- Use the ``update_attributes`` setter method and specify the attribute
names in the value to update the associations.
- Use the ``update()`` method and specify ``<association
name>_attributes`` in the value to update the associations.
- Use the ``create()`` method and specify ``<association
name>_attributes`` in the value to create the associations.

The following example demonstrates how to create a ``Band`` instance
with associated ``album`` records in a single statement:

.. literalinclude:: /includes/data-modeling/nested_attr.rb
:start-after: start-create-attr
:end-before: end-create-attr
:language: ruby
:emphasize-lines: 3-5
:dedent:

Creating Nested Documents
-------------------------

You can create new nested documents by using the nested attributes
feature. When creating a document, omit the ``_id`` field. The following
code uses the ``update()`` method to create a nested ``album`` document
on an existing ``Band`` instance:

.. literalinclude:: /includes/data-modeling/nested_attr.rb
:start-after: start-update-create
:end-before: end-update-create
:language: ruby
:dedent:

This action appends the new document to the existing set, without changing
any existing nested documents.

Updating Nested Documents
-------------------------

You can update existing nested documents by using the nested attributes
feature. To update a nested document, pass the ``_id`` value for the
document to direct {+odm+} to use attributes to update it. The following
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

S: this seems a little clearer to me

Suggested change
feature. To update a nested document, pass the ``_id`` value for the
document to direct {+odm+} to use attributes to update it. The following
feature. To instruct {+odm+} to update a nested document by using attributes, pass the document's ``_id`` value to the ``update()`` method. The following

example uses the ``_id`` value of an ``albums`` entry to update the
``year`` field:

.. literalinclude:: /includes/data-modeling/nested_attr.rb
:start-after: start-update-id
:end-before: end-update-id
:language: ruby
:dedent:

.. note:: No Matching Document

If {+odm+} does not match a document that has the specified ``_id``
value, it raises a ``Mongoid::Errors::DocumentNotFound`` exception.

Delete Nested Documents
-----------------------

You can delete nested documents by specifying the ``_destroy``
attribute to the ``update()`` method. To enable deletion of nested
document, you must set ``allow_destroy: true`` in the

Check failure on line 141 in source/data-modeling/nested-attributes.txt

View workflow job for this annotation

GitHub Actions / TDBX Vale rules

[vale] reported by reviewdog 🐶 [MongoDB.NegativeWords] Use 'remove, delete' instead of the negative word 'destroy'. Raw Output: {"message": "[MongoDB.NegativeWords] Use 'remove, delete' instead of the negative word 'destroy'.", "location": {"path": "source/data-modeling/nested-attributes.txt", "range": {"start": {"line": 141, "column": 32}}}, "severity": "ERROR"}
``accepts_nested_attributes_for`` declaration, as shown in the following
code:

.. code-block:: ruby
:emphasize-lines: 3

class Band
# ...
accepts_nested_attributes_for :albums, allow_destroy: true

Check failure on line 150 in source/data-modeling/nested-attributes.txt

View workflow job for this annotation

GitHub Actions / TDBX Vale rules

[vale] reported by reviewdog 🐶 [MongoDB.NegativeWords] Use 'remove, delete' instead of the negative word 'destroy'. Raw Output: {"message": "[MongoDB.NegativeWords] Use 'remove, delete' instead of the negative word 'destroy'.", "location": {"path": "source/data-modeling/nested-attributes.txt", "range": {"start": {"line": 150, "column": 52}}}, "severity": "ERROR"}
end

The following code uses the ``_destroy`` attribute to delete the first

Check failure on line 153 in source/data-modeling/nested-attributes.txt

View workflow job for this annotation

GitHub Actions / TDBX Vale rules

[vale] reported by reviewdog 🐶 [MongoDB.NegativeWords] Use 'remove, delete' instead of the negative word 'destroy'. Raw Output: {"message": "[MongoDB.NegativeWords] Use 'remove, delete' instead of the negative word 'destroy'.", "location": {"path": "source/data-modeling/nested-attributes.txt", "range": {"start": {"line": 153, "column": 32}}}, "severity": "ERROR"}
``albums`` entry of a ``Band`` instance:

.. literalinclude:: /includes/data-modeling/nested_attr.rb
:start-after: start-delete-id
:end-before: end-delete-id
:language: ruby
:emphasize-lines: 6
:dedent:

.. note:: No Matching Document

If {+odm+} does not match a document that has the specified ``_id``
value, it raises a ``Mongoid::Errors::DocumentNotFound`` exception.

Combine Operations on Nested Documents
--------------------------------------

You can perform multiple data operations on nested documents by using
the nested attributes feature.

The following code creates a nested document, updates an existing
document, and deletes a document in the ``albums`` array of a ``Band``
instance:

.. literalinclude:: /includes/data-modeling/nested_attr.rb
:start-after: start-multiple-ops
:end-before: end-multiple-ops
:language: ruby
:dedent:

Additional Information
----------------------

To learn more about querying, see :ref:`mongoid-data-specify-query`.

.. TODO link to CRUD guide

.. TODO link to associations guide
58 changes: 58 additions & 0 deletions source/includes/data-modeling/nested_attr.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# start-simple-nested
class Band
include Mongoid::Document
embeds_many :albums
belongs_to :producer
accepts_nested_attributes_for :albums, :producer
end
# end-simple-nested

#start-use-method
# Retrieves a Band instance
band = Band.where(name: 'Tennis').first
# Updates the "producer" association
band.producer_attributes = { name: 'Alaina Moore' }
#end-use-method

# start-create-attr
band = Band.create(
name: 'Tennis',
albums_attributes: [
{ name: 'Swimmer', year: 2020 },
{ name: 'Young & Old', year: 2013 }]
)
# end-create-attr

# start-update-create
band = Band.where(name: 'Vampire Weekend').first
band.update(albums_attributes: [
{ name: 'Contra', year: 2010 }
])
# end-update-create

# start-update-id
band = Band.where(name: 'Vampire Weekend').first
# Retrieves the first entry from the albums array
album = band.albums.first
# Updates the entry by passing the _id value
band.update(albums_attributes: [
{ _id: album._id, year: 2011 } ])
# end-update-id

# start-delete-id
band = Band.where(name: 'Vampire Weekend').first
# Retrieves the first entry from the albums array
album = band.albums.first
# Deletes the entry by passing the _id value
band.update(albums_attributes: [
{ _id: album._id, _destroy: true } ])
# end-delete-id

# start-multiple-ops
band = Band.where(name: 'Yeah Yeah Yeahs').first
# Performs multiple data changes
band.update(albums_attributes: [
{ name: 'Show Your Bones', year: 2006 },
{ _id: 1, name: 'Fever To T3ll' },
{ _id: 2, _destroy: true } ])
# end-multiple-ops
Loading