Compare commits

...

2 Commits

Author SHA1 Message Date
Stefan Wojcik
a659f9aa8d Add a changelog entry [ci skip] 2019-06-21 15:59:04 +02:00
Stefan Wojcik
f884839d17 Implement BaseDocument.to_dict
`BaseDocument.to_dict` serializes a document/embedded document into a dict,
which can be easily consumed by other modules (which in this case don't need
to be aware of MongoEngine-specific object types).

The output dict contains key-value pairs where:
* Keys are field names as they're defined on the document (as opposed to e.g.
  how they're stored in MongoDB).
* Values are field values in their deserialized form (i.e. the same form that
  you get when you access `doc.some_field_name`).
2019-06-21 15:45:33 +02:00
3 changed files with 103 additions and 9 deletions

View File

@ -6,6 +6,7 @@ Changelog
Development Development
=========== ===========
- (Fill this out as you fix issues and develop your features). - (Fill this out as you fix issues and develop your features).
- Add a `BaseDocument.to_dict` method #2101
Changes in 0.18.1 Changes in 0.18.1
================= =================

View File

@ -309,9 +309,7 @@ class BaseDocument(object):
return self._data['_text_score'] return self._data['_text_score']
def to_mongo(self, use_db_field=True, fields=None): def to_mongo(self, use_db_field=True, fields=None):
""" """Return as SON data ready for use with MongoDB."""
Return as SON data ready for use with MongoDB.
"""
fields = fields or [] fields = fields or []
data = SON() data = SON()
@ -412,12 +410,35 @@ class BaseDocument(object):
message = 'ValidationError (%s:%s) ' % (self._class_name, pk) message = 'ValidationError (%s:%s) ' % (self._class_name, pk)
raise ValidationError(message, errors=errors) raise ValidationError(message, errors=errors)
def to_dict(self):
"""Serialize this document into a dict.
Return field names as they're defined on the document (as opposed to
e.g. how they're stored in MongoDB). Return values in their
deserialized form (i.e. the same form that you get when you access
`doc.some_field_name`). Serialize embedded documents recursively.
The resultant dict can be consumed easily by other modules which
don't need to be aware of MongoEngine-specific object types.
:return dict: dictionary of field name & value pairs.
"""
data_dict = {}
for field_name in self._fields:
value = getattr(self, field_name)
if isinstance(value, BaseDocument):
data_dict[field_name] = value.to_dict()
else:
data_dict[field_name] = value
return data_dict
def to_json(self, *args, **kwargs): def to_json(self, *args, **kwargs):
"""Convert this document to JSON. """Convert this document to JSON.
:param use_db_field: Serialize field names as they appear in :param use_db_field: Serialize field names as they appear in
MongoDB (as opposed to attribute names on this document). MongoDB (as opposed to attribute names on this document).
Defaults to True. Defaults to True.
:return str: string representing the jsonified document.
""" """
use_db_field = kwargs.pop('use_db_field', True) use_db_field = kwargs.pop('use_db_field', True)
return json_util.dumps(self.to_mongo(use_db_field), *args, **kwargs) return json_util.dumps(self.to_mongo(use_db_field), *args, **kwargs)
@ -426,12 +447,13 @@ class BaseDocument(object):
def from_json(cls, json_data, created=False): def from_json(cls, json_data, created=False):
"""Converts json data to a Document instance """Converts json data to a Document instance
:param json_data: The json data to load into the Document :param str json_data: The json data to load into the Document
:param created: If True, the document will be considered as a brand new document :param bool created: If True, the document will be considered as
If False and an id is provided, it will consider that the data being a brand new document. If False and an ID is provided, it will
loaded corresponds to what's already in the database (This has an impact of subsequent call to .save()) consider that the data being loaded corresponds to what's already
If False and no id is provided, it will consider the data as a new document in the database (This has an impact of subsequent call to .save())
(default ``False``) If False and no id is provided, it will consider the data as a new
document (default ``False``)
""" """
return cls._from_son(json_util.loads(json_data), created=created) return cls._from_son(json_util.loads(json_data), created=created)

View File

@ -3525,5 +3525,76 @@ class InstanceTest(MongoDBTestCase):
User.objects().select_related() User.objects().select_related()
class DocumentToDictTest(MongoDBTestCase):
"""Class for testing the BaseDocument.to_dict method."""
def test_to_dict(self):
class Person(Document):
name = StringField()
age = IntField()
p = Person(name='Tom', age=30)
self.assertEqual(p.to_dict(), {'id': None, 'name': 'Tom', 'age': 30})
def test_to_dict_with_a_persisted_doc(self):
class Person(Document):
name = StringField()
age = IntField()
p = Person.objects.create(name='Tom', age=30)
p_dict = p.to_dict()
self.assertTrue(p_dict['id'])
self.assertEqual(p_dict['name'], 'Tom')
self.assertEqual(p_dict['age'], 30)
def test_to_dict_empty_doc(self):
class Person(Document):
name = StringField()
age = IntField()
p = Person()
self.assertEqual(p.to_dict(), {'id': None, 'name': None, 'age': None})
def test_to_dict_with_default_values(self):
class Person(Document):
name = StringField(default='Unknown')
age = IntField(default=0)
p = Person()
self.assertEqual(
p.to_dict(),
{'id': None, 'name': 'Unknown', 'age': 0}
)
def test_to_dict_with_a_db_field(self):
class Person(Document):
name = StringField(db_field='db_name')
p = Person(name='Tom')
self.assertEqual(p.to_dict(), {'id': None, 'name': 'Tom'})
def test_to_dict_with_a_primary_key(self):
class Person(Document):
username = StringField(primary_key=True)
p = Person(username='tomtom')
self.assertEqual(p.to_dict(), {'username': 'tomtom'})
def test_to_dict_with_an_embedded_document(self):
class Book(EmbeddedDocument):
title = StringField()
class Author(Document):
name = StringField()
book = EmbeddedDocumentField(Book)
a = Author(name='Yuval', book=Book(title='Sapiens'))
self.assertEqual(a.to_dict(), {
'id': None,
'name': 'Yuval',
'book': {'title': 'Sapiens'}
})
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()