15

I'm learning MongoDB through the Mongoid Ruby gem with Rails (Rails 3 beta 3), and I'm trying to come up with a way to create dynamic attributes on a model based on fields from another model, which I thought a schema-less database would be a good choice for.

So for example, I'd have the models:

class Account
  include Mongoid::Document

  field :name, :type => String
  field :token, :type => String
  field :info_needed, :type => Array

  embeds_many :members
end

class Member
  include Mongoid::Document

  embedded_in :account, :inverse_of => :members

end

I'm looking to take the "info_needed" attribute of the Account model and created dynamic attributes on the Member model based on what's inside. If club.info_needed was ["first_name", "last_name"], I'm trying to create a form that would save first_name and last_name attributes to the Member model.

However, upon practice, I just keep getting "undefined method first_name=" errors on the Member model when trying to do this. I know MongoDB can handle dynamic attributes per record, but how can I get Mongoid to do this without an undefined method error?

joeellis
  • 2,745
  • 7
  • 28
  • 45
  • 1
    I'd ask that you select my answer below as the answer as it's more accurate than the currently selected answer. – Ben Zittlau Mar 01 '13 at 17:34

4 Answers4

30

Mongoid now supports Dynamic Fields. Their documentation can be found here: http://mongoid.org/en/mongoid/docs/documents.html#dynamic_fields

Basically it warns that you have to be slightly careful how you set dynamic fields as it will raise a no method error if you attempt to use the getter and setter methods for a field that did not exist in the document.

[],[]= are shortcuts for read_attribute(),write_attribute() , and should be used if you do not set dynamic_attributes = true in your ./config/mongoid.yml file , otherwise you'll get a no method error.

Setting allow_dynamic_fields: true can be risky, as you might pollute your data/schema with unintended fields caused by bugs in your code. It's probably safer to set this to false and explicitly use [],[]=

# Raise a NoMethodError if value isn't set.
person.gender
person.gender = "Male"

# Retrieve a dynamic field safely.
person[:gender]
person.read_attribute(:gender)

# Write a dynamic field safely.
person[:gender] = "Male"
person.write_attribute(:gender, "Male")
Tilo
  • 33,354
  • 5
  • 79
  • 106
Ben Zittlau
  • 2,345
  • 1
  • 21
  • 30
8

Be sure to set allow_dynamic_fields: true in mongoid.yml. Example:

defaults: &defaults
  allow_dynamic_fields: true
  parameterize_keys: true
  persist_in_safe_mode: true
  raise_not_found_error: true
  reconnect_time: 3
  use_object_ids: false

development:
  <<: *defaults

...
ryeguy
  • 65,519
  • 58
  • 198
  • 260
gucki
  • 4,582
  • 7
  • 44
  • 56
  • Yea, I had done that, but was still having trouble. If you've done this though, and it's been working for you, perhaps mongoid was updated since I posted this question. – joeellis May 20 '10 at 15:29
  • 1
    downside to setting `allow_dynamic_attributes: true` is that it is per database, and that you might pollute your schema with unintended fields, caused through bugs in your code. Probably safer to set this to `false` and to consciously use [],[]= – Tilo Jul 23 '12 at 00:16
2

interesting article about Dynamic Attributes with Mongoid and Rails 3.1: http://paul-wong-jr.blogspot.com/2012/03/dynamic-attributes-and-mongodbmongoid.html

To access only the dynamic key/value pairs or dynamic attribute names, see also: List dynamic attributes in a Mongoid Model

Community
  • 1
  • 1
Tilo
  • 33,354
  • 5
  • 79
  • 106
1

Mongoid doesn't really support it.

I happen to have asked this at Mongoid group myself.

It is possible when you create new document, like this:

account = Account.new(:some_dynamic_field => "...")

Vojto
  • 6,901
  • 4
  • 27
  • 33