Skip to content

Commit 895424a

Browse files
authored
DOCSP-45360: nested attributes (#71)
* DOCSP-45360: nested attributes * vale + fixes * fixes * NR PR fixes 1
1 parent 95545a2 commit 895424a

File tree

3 files changed

+254
-0
lines changed

3 files changed

+254
-0
lines changed

source/data-modeling.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Model Your Data
1616

1717
Documents </data-modeling/documents>
1818
Inheritance </data-modeling/inheritance>
19+
Nested Attributes </data-modeling/nested-attributes>
1920

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

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

2526
- :ref:`mongoid-modeling-inheritance`: Learn how to implement
2627
inheritance in your model classes.
28+
29+
- :ref:`mongoid-modeling-nested-attr`: Learn how to modify documents and
30+
their associations in a single operation.
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
.. _mongoid-modeling-nested-attr:
2+
3+
=================
4+
Nested Attributes
5+
=================
6+
7+
.. facet::
8+
:name: genre
9+
:values: reference
10+
11+
.. meta::
12+
:keywords: ruby framework, odm, embeddings, code example, queries
13+
14+
.. contents:: On this page
15+
:local:
16+
:backlinks: none
17+
:depth: 2
18+
:class: singlecol
19+
20+
Overview
21+
--------
22+
23+
In this guide, you can learn how to define **nested attributes** on
24+
models to enable data operations on documents and their associations.
25+
After you define a nested attribute, you can specify updates to
26+
top-level and associated documents in a single parameter hash. This might be
27+
useful if your application requires editing multiple documents within a single
28+
form.
29+
30+
Behavior
31+
--------
32+
33+
You can enable nested attributes for any association, embedded or
34+
referenced. To add a nested attribute for an association, provide the
35+
association name to the ``accepts_nested_attributes_for`` macro when
36+
defining a model class.
37+
38+
The following code defines embedded associations on the ``Band`` model
39+
class and includes the ``accepts_nested_attributes_for`` macro:
40+
41+
.. literalinclude:: /includes/data-modeling/nested_attr.rb
42+
:start-after: start-simple-nested
43+
:end-before: end-simple-nested
44+
:language: ruby
45+
:emphasize-lines: 5
46+
:dedent:
47+
48+
.. note:: Autosave Enabled
49+
50+
When you add nested attribute functionality to a referenced
51+
association, {+odm+} automatically enables autosave for that
52+
association.
53+
54+
When you enable nested attributes behavior on an association, {+odm+}
55+
adds a special method to the base model. You can use this method to
56+
update the attributes.
57+
58+
The method name is the association name suffixed with ``_attributes``. For
59+
example, the setter method to update the ``producers`` association is
60+
``producer_attributes``.
61+
62+
You can use this method directly, or you can use the name of the method
63+
as an attribute in the updates for the top-level class. In this case,
64+
{+odm+} calls the appropriate setter method internally.
65+
66+
The following code retrieves an instance of ``Band``, then uses the
67+
nested attribute update method ``producer_attributes`` to set a value
68+
for the association document:
69+
70+
.. literalinclude:: /includes/data-modeling/nested_attr.rb
71+
:start-after: start-use-method
72+
:end-before: end-use-method
73+
:language: ruby
74+
:emphasize-lines: 4
75+
:dedent:
76+
77+
There are multiple ways to update a nested attribute:
78+
79+
- Use the ``<association name>_attributes`` setter method.
80+
- Use the ``attributes`` setter method and specify ``<association
81+
name>_attributes`` in the value to update the associations.
82+
- Use the ``update_attributes`` setter method and specify the attribute
83+
names in the value to update the associations.
84+
- Use the ``update()`` method and specify ``<association
85+
name>_attributes`` in the value to update the associations.
86+
- Use the ``create()`` method and specify ``<association
87+
name>_attributes`` in the value to create the associations.
88+
89+
The following example demonstrates how to create a ``Band`` instance
90+
with associated ``album`` records in a single statement:
91+
92+
.. literalinclude:: /includes/data-modeling/nested_attr.rb
93+
:start-after: start-create-attr
94+
:end-before: end-create-attr
95+
:language: ruby
96+
:emphasize-lines: 3-5
97+
:dedent:
98+
99+
Creating Nested Documents
100+
-------------------------
101+
102+
You can create new nested documents by using the nested attributes
103+
feature. When creating a document, omit the ``_id`` field. The following
104+
code uses the ``update()`` method to create a nested ``album`` document
105+
on an existing ``Band`` instance:
106+
107+
.. literalinclude:: /includes/data-modeling/nested_attr.rb
108+
:start-after: start-update-create
109+
:end-before: end-update-create
110+
:language: ruby
111+
:dedent:
112+
113+
This action appends the new document to the existing set without changing
114+
any existing nested documents.
115+
116+
Updating Nested Documents
117+
-------------------------
118+
119+
You can update existing nested documents by using the nested attributes
120+
feature. To instruct {+odm+} to update a nested document by using
121+
attributes, pass the document's ``_id`` value to the ``update()``
122+
method. The following example uses the ``_id`` value of an ``albums``
123+
entry to update the ``year`` field:
124+
125+
.. literalinclude:: /includes/data-modeling/nested_attr.rb
126+
:start-after: start-update-id
127+
:end-before: end-update-id
128+
:language: ruby
129+
:dedent:
130+
131+
.. important:: No Matching Document
132+
133+
If {+odm+} does not match a document that has the specified ``_id``
134+
value, it raises a ``Mongoid::Errors::DocumentNotFound`` exception.
135+
136+
Delete Nested Documents
137+
-----------------------
138+
139+
You can delete nested documents by specifying the ``_destroy``
140+
attribute to the ``update()`` method. To enable deletion of nested
141+
document, you must set ``allow_destroy: true`` in the
142+
``accepts_nested_attributes_for`` declaration, as shown in the following
143+
code:
144+
145+
.. code-block:: ruby
146+
:emphasize-lines: 3
147+
148+
class Band
149+
# ...
150+
accepts_nested_attributes_for :albums, allow_destroy: true
151+
end
152+
153+
The following code uses the ``_destroy`` attribute to delete the first
154+
``albums`` entry of a ``Band`` instance:
155+
156+
.. literalinclude:: /includes/data-modeling/nested_attr.rb
157+
:start-after: start-delete-id
158+
:end-before: end-delete-id
159+
:language: ruby
160+
:emphasize-lines: 6
161+
:dedent:
162+
163+
.. important:: No Matching Document
164+
165+
If {+odm+} does not match a document that has the specified ``_id``
166+
value, it raises a ``Mongoid::Errors::DocumentNotFound`` exception.
167+
168+
Combine Operations on Nested Documents
169+
--------------------------------------
170+
171+
You can perform multiple data operations on nested documents by using
172+
the nested attributes feature.
173+
174+
The following code creates a nested document, updates an existing
175+
document, and deletes a document in the ``albums`` array of a ``Band``
176+
instance:
177+
178+
.. literalinclude:: /includes/data-modeling/nested_attr.rb
179+
:start-after: start-multiple-ops
180+
:end-before: end-multiple-ops
181+
:language: ruby
182+
:dedent:
183+
184+
Additional Information
185+
----------------------
186+
187+
To learn more about querying, see the :ref:`mongoid-data-specify-query`
188+
guide.
189+
190+
.. TODO link to CRUD guide
191+
192+
.. TODO link to associations guide
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# start-simple-nested
2+
class Band
3+
include Mongoid::Document
4+
embeds_many :albums
5+
belongs_to :producer
6+
accepts_nested_attributes_for :albums, :producer
7+
end
8+
# end-simple-nested
9+
10+
#start-use-method
11+
# Retrieves a Band instance
12+
band = Band.where(name: 'Tennis').first
13+
# Updates the "producer" association
14+
band.producer_attributes = { name: 'Alaina Moore' }
15+
#end-use-method
16+
17+
# start-create-attr
18+
band = Band.create(
19+
name: 'Tennis',
20+
albums_attributes: [
21+
{ name: 'Swimmer', year: 2020 },
22+
{ name: 'Young & Old', year: 2013 }]
23+
)
24+
# end-create-attr
25+
26+
# start-update-create
27+
band = Band.where(name: 'Vampire Weekend').first
28+
band.update(albums_attributes: [
29+
{ name: 'Contra', year: 2010 }
30+
])
31+
# end-update-create
32+
33+
# start-update-id
34+
band = Band.where(name: 'Vampire Weekend').first
35+
# Retrieves the first entry from the albums array
36+
album = band.albums.first
37+
# Updates the entry by passing the _id value
38+
band.update(albums_attributes: [
39+
{ _id: album._id, year: 2011 } ])
40+
# end-update-id
41+
42+
# start-delete-id
43+
band = Band.where(name: 'Vampire Weekend').first
44+
# Retrieves the first entry from the albums array
45+
album = band.albums.first
46+
# Deletes the entry by passing the _id value
47+
band.update(albums_attributes: [
48+
{ _id: album._id, _destroy: true } ])
49+
# end-delete-id
50+
51+
# start-multiple-ops
52+
band = Band.where(name: 'Yeah Yeah Yeahs').first
53+
# Performs multiple data changes
54+
band.update(albums_attributes: [
55+
{ name: 'Show Your Bones', year: 2006 },
56+
{ _id: 1, name: 'Fever To T3ll' },
57+
{ _id: 2, _destroy: true } ])
58+
# end-multiple-ops

0 commit comments

Comments
 (0)