Make the MongoDB docs better! We value your opinion. Share your feedback for a chance to win $100.
Click here >
Docs Menu
Docs Home
/ /

Customize Association Behavior

Associations in Mongoid allow you to create relationships between models. In this guide, you can learn how to use Mongoid to customize how associations behave in your application. The following sections describe ways to customize association behaviors.

Extensions allow you to add custom functionality to an association. You can define an extension on an association by specifying a block in the association definition, as shown in the following example:

class Band
include Mongoid::Document
embeds_many :albums do
def find_by_name(name)
where(name: name).first
end
end
end
band.albums.find_by_name("Omega") # returns album "Omega"

You can use the class_name macro to specify a custom class name for an association. This is useful when you want to name the association something other than the name of the class. The following example uses the class_name macro to specify that an embedded association called records represents the Album class:

class Band
include Mongoid::Document
embeds_many :records, class_name: "Album"
end

By default, Mongoid uses the _id field of the parent class when looking up associations. You can specify different fields to use by using the primary_key and foreign_key macros. The following example specifies a new primary and foreign key for the albums association on a Band class:

class Band
include Mongoid::Document
field :band_id, type: String
has_many :albums, primary_key: 'band_id', foreign_key: 'band_id_ref'
end
class Album
include Mongoid::Document
field :band_id_ref, type: String
belongs_to :band, primary_key: 'band_id', foreign_key: 'band_id_ref'
end

If you are specifying a has_and_belongs_to_many association, you can also use the inverse_primary_key and inverse_foreign_key macros. The inverse_primary_key macro specifies the field on the local model that the remote model uses to look up the documents. The inverse_foreign_key macro specifies the field on the remote model that stores the values found in inverse_primary_key.

The following example specifies a new primary and foreign key for the Band and Members classes in a has_and_belongs_to_many association:

class Band
include Mongoid::Document
field :band_id, type: String
field :member_ids, type: Array
has_many :members,
primary_key: 'member_id', foreign_key: 'member_ids',
inverse_primary_key: 'band_id', inverse_foreign_key: 'band_ids'
end
class Member
include Mongoid::Document
field :member_id, type: String
field :band_ids, type: Array
has_many :bands,
primary_key: 'band_id', foreign_key: 'band_ids',
inverse_primary_key: 'member_id', inverse_foreign_key: 'member_ids'
end

You can specify the scope of an association by using the scope parameter. The scope parameter determines the documents that Mongoid considers part of an association. A scoped association returns only documents that match the scope conditions when queried. You can set the scope to either a Proc with an arity of zero or a Symbol that references a named scope on the associated model. The following example sets custom scopes on associations in a Band class:

class Band
include Mongoid::Document
has_many :albums, scope: -> { where(published: true) }
# Uses a scope called "upcoming" on the Tour model
has_many :tours, scope: :upcoming
end

Note

You can add documents that do not match the scope conditions to an association. Mongoid saves the documents to the database and they will appear in associated memory. However, you won't see the documents when querying the association.

When Mongoid loads an association into memory, by default, it uses the validates_associated macro to validate that any children are also present. Mongoid validates children for the following association types:

  • embeds_many

  • embeds_one

  • has_many

  • has_one

  • has_and_belongs_to_many

You can turn off this validation behavior by setting the validate macro to false when defining the association, as shown in the following example:

class Band
include Mongoid::Document
embeds_many :albums, validate: false
end

Mongoid supports polymorphism on the child classes of one-to-one and one-to-many associations. Polymorphic associations allows a single association to contain objects of different class types. You can define a polymorphic association by setting the polymorphic option to true in a child association and adding the as option to the parent association. The following example creates a polymorphic association in a Band class:

class Tour
include Mongoid::Document
has_one :band, as: :featured
end
class Label
include Mongoid::Document
has_one :band, as: :featured
end
class Band
include Mongoid::Document
belongs_to :featured, polymorphic: true
end

In the preceding example, the :featured association in the Band class can contain either a Label or Album document.

Important

Mongoid supports polymorphism only from child to parent. You cannot specify a parent has_one or has_many association as polymorphic.

has_and_belongs_to_many associations do not support polymorphism.

Starting in version 9.0.2, Mongoid supports custom polymorphic types through a global registry. You can specify alternative keys to represent different classes, decoupling your code from the data. The following example specifies the string "artist" as an alternate key for the Band class:

class Band
include Mongoid::Document
identify_as 'artist'
has_many :albums, as: :record
end

In the preceding example, the identify_as directive instructs Mongoid to store the Band class in the database as the string "artist".

You can also specify multiple aliases, as shown in the following example:

class Band
include Mongoid::Document
identify_as 'artist', 'group', 'troupe'
has_many :albums, as: :record
end

In the preceding example, artist is the default name and the others are used only for looking up records. This allows you to refactor your code without breaking the associations in your data.

Polymorphic type aliases are global. The keys you specify must be unique across your entire code base. However, you can register alternative resolvers that can be used for different subsets of your models. In this case, the keys must be unique only for each resolver. The following example shows how to register alternate resolvers:

Mongoid::ModelResolver.register_resolver Mongoid::ModelResolver.new, :mus
Mongoid::ModelResolver.register_resolver Mongoid::ModelResolver.new, :tool
module Music
class Band
include Mongoid::Document
identify_as 'bnd', resolver: :mus
end
end
module Tools
class Band
include Mongoid::Document
identify_as 'bnd', resolver: :tool
end
end

Both Music::Band and Tools::Band are aliased as "bnd", but each model uses its own resolver to avoid conflicts.

You can provide dependent options to referenced associations to specify how Mongoid handles associated documents when a document is deleted. You can specify the following options:

  • delete_all: Deletes all child documents without running any model callbacks.

  • destroy: Deletes the child documents and runs all model callbacks.

  • nullify: Sets the foreign key of the child documents to nil. The child document might become orphaned if it is referenced by only the parent.

  • restrict_with_exception: Raises an exception if the child document is not empty.

  • restrict_with_error: Cancels the operation and returns false if the child document is not empty.

If you don't specify any dependent options, Mongoid leaves the child document unchanged when the parent document is deleted. The child document continues to reference the deleted parent document, and if it is referenced through only the parent, the child document becomes orphaned.

The following example specifies dependent options on the Band class:

class Band
include Mongoid::Document
has_many :albums, dependent: :delete_all
belongs_to :label, dependent: :nullify
end

By default, Mongoid does not automatically save associated documents from non-embedded associations when saving the parent document. This can result in dangling references to documents that don't exist.

You can use the autosave option on a referenced association to automatically save associated documents when saving the parent document. The following example creates a Band class with an associated Album class and specifies the autosave option:

class Band
include Mongoid::Document
has_many :albums
end
class Album
include Mongoid::Document
belongs_to :band, autosave: true
end
band = Band.new
album = Album.create!(band: band)
# The band is persisted at this point.

Note

Mongoid automatically adds autosave functionality to an association that uses the accepts_nested_attributes_for option.

You do not need to specify the autosave option for embedded associations because Mongoid saves embedded documents in the parent document.

You can add the autobuild option to one-to-one associations, such as has_one and embeds_one, to automatically instantiate a new document when accessing a nil association. The following example adds the autobuild option to an association on the Band class:

class Band
include Mongoid::Document
embeds_one :label, autobuild: true
has_one :producer, autobuild: true
end

When Mongoid touches a document, it updates the document's updated_at field to the current date and time. You can add the touch option to any belongs_to association to ensure that Mongoid touches the parent document whenever the child document is updated. The following example adds the touch option to an association on the Band class:

class Band
include Mongoid::Document
field :name
belongs_to :label, touch: true
end

You can also use the touch option to specify another field on the parent association, as a string or a symbol. When Mongoid touches the parent association, it sets both the updated_at field and the specified field to the current date and time.

The following example instructs Mongoid to touch the bands_updated_at field:

class Band
include Mongoid::Document
belongs_to :label, touch: :bands_updated_at
end

Note

In embedded associations, when an embedded document is touched, Mongoid touches its parents recursively. Because of this, adding a touch attribute to an embedded_in association is unnecessary.

Mongoid does not support specifying additional fields to touch in embedded_in associations.

Use the counter_cache option to store the number of objects that belong to an associated field. When you specify this option, Mongoid stores a counter field on the associated parent model.

The counter_cache option accepts one of the two following values for the counter field name:

  • true: Mongoid uses a default counter field name, based on the name of the association. For example, if the association is called Band, the default counter field name is bands_count.

  • A string or symbol: Mongoid uses that value directly as the counter field name.

You can provide the container for the counter cache in one of the following ways:

  • Define an explicit counter field in the parent model.

  • Include the Mongoid::Attributes::Dynamic module in the parent model, which allows Mongoid to create the counter field dynamically on first use.

Explicit Counter Field

In this approach, declare the counter field directly on the parent model. This is useful when you want a fixed schema and explicit field types.

The following example declares an explicit bands_count field on the Label class and uses the default counter field name:

class Band
include Mongoid::Document
# Uses the default counter field name: bands_count
belongs_to :label, counter_cache: true
end
class Label
include Mongoid::Document
field :bands_count, type: Integer
has_many :bands
end

You can also specify a custom counter field name by passing a string to the counter_cache option:

class Band
include Mongoid::Document
belongs_to :label, counter_cache: :active_bands_count
end
class Label
include Mongoid::Document
field :active_bands_count, type: Integer
has_many :bands
end

Dynamic Counter Field

If you include the Mongoid::Attributes::Dynamic module in the parent model, Mongoid can create and update the counter field dynamically during runtime. This is useful when you want a flexible schema and don't want to declare the counter field explicitly.

The following example uses Mongoid::Attributes::Dynamic to allow Mongoid to create the counter field dynamically on the Label class:

class Band
include Mongoid::Document
belongs_to:label, counter_cache: true
end
class Label
include Mongoid::Document
include Mongoid::Attributes::Dynamic
has_many :bands
end

When you use the Mongoid::Attributes::Dynamic module, Mongoid uses the same naming rules for the counter field as when you declare an explicit counter field: it defaults to a *_count field name based on the association, unless you specify a custom field name with a string in the counter_cache option.

Back

Embedded Associations

On this page