Here is a function to update documents with EmbeddedDocuments. It is based upon @rednaw's solution, though accounts for EmbeddedDocuments having EmbeddedDocuments.
from mongoengine.fields import *
def field_value(field, value):
'''
Converts a supplied value to the type required by the field.
If the field requires a EmbeddedDocument the EmbeddedDocument
is created and updated using the supplied data.
'''
if field.__class__ in (ListField, SortedListField):
# return a list of the field values
return [
field_value(field.field, item)
for item in value]
elif field.__class__ in (
EmbeddedDocumentField,
GenericEmbeddedDocumentField,
ReferenceField,
GenericReferenceField):
embedded_doc = field.document_type()
update_document(embedded_doc, value)
return embedded_doc
else:
return value
def update_document(doc, data):
''' Update an document to match the supplied dictionary.
'''
for key, value in data.iteritems():
if hasattr(doc, key):
value = field_value(doc._fields[key], value)
setattr(doc, key, value)
else:
# handle invalid key
pass
return doc
The key here is the field_value
method updating an embedded document rather than instantiating it with the data.
Usage Example :
class Pets(EmbeddedDocument):
name = StringField()
class Person(EmbeddedDocument):
name = StringField()
address = StringField()
pets = ListField(EmbeddedDocumentField(Pets))
class Group(Document):
name = StringField()
members = ListField(EmbeddedDocumentField(Person))
g = Group()
update_document(g, {
'name': 'Coding Buddies',
'members': [
{
'name': 'Dawson',
'address': 'Somewhere in Nova Scotia',
'pets': [
{
'name': 'Sparkles'
}
]
},
{
'name': 'rednaw',
'address': 'Not too sure?',
'pets': [
{
'name': 'Fluffy'
}
]
}
]
})
FYI That's actually my cat's name.
EDIT : typo in variable names.
create
?p = Person()
instantiates the object with default and doesn't fetch an existing object from the DB to then update the attributes. – Dowlen