Docs Menu

Docs HomeMongoid

Nested Attributes

On this page

  • Behavior
  • Creating Records
  • Updating Records
  • Destroying Records
  • Combining Operations

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.

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.

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.

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:

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

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

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.

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

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.

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:

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.

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:

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 } ])
←  Persistence ConfigurationCallbacks →