diff --git a/mongoengine/base/document.py b/mongoengine/base/document.py index 0c2f3d7c..961f5be1 100644 --- a/mongoengine/base/document.py +++ b/mongoengine/base/document.py @@ -257,7 +257,7 @@ class BaseDocument(object): """ pass - def to_mongo(self): + def to_mongo(self, use_db_field=True): """Return as SON data ready for use with MongoDB. """ data = SON() @@ -271,7 +271,11 @@ class BaseDocument(object): field = self._dynamic_fields.get(field_name) if value is not None: - value = field.to_mongo(value) + EmbeddedDocument = _import_class("EmbeddedDocument") + if isinstance(value, (EmbeddedDocument)) and use_db_field==False: + value = field.to_mongo(value, use_db_field) + else: + value = field.to_mongo(value) # Handle self generating fields if value is None and field._auto_gen: @@ -279,7 +283,10 @@ class BaseDocument(object): self._data[field_name] = value if value is not None: - data[field.db_field] = value + if use_db_field: + data[field.db_field] = value + else: + data[field.name] = value # If "_id" has not been set, then try and set it Document = _import_class("Document") @@ -342,8 +349,11 @@ class BaseDocument(object): raise ValidationError(message, errors=errors) def to_json(self, *args, **kwargs): - """Converts a document to JSON""" - return json_util.dumps(self.to_mongo(), *args, **kwargs) + """Converts a document to JSON. + :param use_db_field: Set to True by default but enables the output of the json structure with the field names and not the mongodb store db_names in case of set to False + """ + use_db_field = kwargs.pop('use_db_field') if kwargs.has_key('use_db_field') else True + return json_util.dumps(self.to_mongo(use_db_field), *args, **kwargs) @classmethod def from_json(cls, json_data): diff --git a/mongoengine/fields.py b/mongoengine/fields.py index 5e6bf604..0320898b 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -311,7 +311,7 @@ class DecimalField(BaseField): return value return value.quantize(self.precision, rounding=self.rounding) - def to_mongo(self, value): + def to_mongo(self, value, use_db_field=True): if value is None: return value if self.force_string: @@ -551,10 +551,10 @@ class EmbeddedDocumentField(BaseField): return self.document_type._from_son(value) return value - def to_mongo(self, value): + def to_mongo(self, value, use_db_field=True): if not isinstance(value, self.document_type): return value - return self.document_type.to_mongo(value) + return self.document_type.to_mongo(value, use_db_field) def validate(self, value, clean=True): """Make sure that the document instance is an instance of the @@ -601,11 +601,11 @@ class GenericEmbeddedDocumentField(BaseField): value.validate(clean=clean) - def to_mongo(self, document): + def to_mongo(self, document, use_db_field=True): if document is None: return None - data = document.to_mongo() + data = document.to_mongo(use_db_field) if not '_cls' in data: data['_cls'] = document._class_name return data @@ -628,7 +628,7 @@ class DynamicField(BaseField): cls = value.__class__ val = value.to_mongo() # If we its a document thats not inherited add _cls - if (isinstance(value, Document)): + if (isinstance(value, Document)): val = {"_ref": value.to_dbref(), "_cls": cls.__name__} if (isinstance(value, EmbeddedDocument)): val['_cls'] = cls.__name__ @@ -1001,7 +1001,7 @@ class GenericReferenceField(BaseField): doc = doc_cls._from_son(doc) return doc - def to_mongo(self, document): + def to_mongo(self, document, use_db_field=True): if document is None: return None @@ -1567,7 +1567,7 @@ class SequenceField(BaseField): def prepare_query_value(self, op, value): """ This method is overriden in order to convert the query value into to required - type. We need to do this in order to be able to successfully compare query + type. We need to do this in order to be able to successfully compare query values passed as string, the base implementation returns the value as is. """ return self.value_decorator(value) @@ -1631,12 +1631,12 @@ class UUIDField(BaseField): class GeoPointField(BaseField): - """A list storing a longitude and latitude coordinate. + """A list storing a longitude and latitude coordinate. - .. note:: this represents a generic point in a 2D plane and a legacy way of - representing a geo point. It admits 2d indexes but not "2dsphere" indexes - in MongoDB > 2.4 which are more natural for modeling geospatial points. - See :ref:`geospatial-indexes` + .. note:: this represents a generic point in a 2D plane and a legacy way of + representing a geo point. It admits 2d indexes but not "2dsphere" indexes + in MongoDB > 2.4 which are more natural for modeling geospatial points. + See :ref:`geospatial-indexes` .. versionadded:: 0.4 """ diff --git a/tests/document/json_serialisation.py b/tests/document/json_serialisation.py index 2b5d9a0c..fd7795f7 100644 --- a/tests/document/json_serialisation.py +++ b/tests/document/json_serialisation.py @@ -20,6 +20,28 @@ class TestJson(unittest.TestCase): def setUp(self): connect(db='mongoenginetest') + def test_json_names(self): + """ + Going to test reported issue: + https://github.com/MongoEngine/mongoengine/issues/654 + where the reporter asks for the availability to perform + a to_json with the original class names and not the abreviated + mongodb document keys + """ + class Embedded(EmbeddedDocument): + string = StringField(db_field='s') + + class Doc(Document): + string = StringField(db_field='s') + embedded = EmbeddedDocumentField(Embedded, db_field='e') + + doc = Doc( string="Hello", embedded=Embedded(string="Inner Hello")) + doc_json = doc.to_json(sort_keys=True, use_db_field=False,separators=(',', ':')) + + expected_json = """{"embedded":{"string":"Inner Hello"},"string":"Hello"}""" + + self.assertEqual( doc_json, expected_json) + def test_json_simple(self): class Embedded(EmbeddedDocument):