diff --git a/mongoengine/document.py b/mongoengine/document.py index a2a5e156..b79e5e97 100644 --- a/mongoengine/document.py +++ b/mongoengine/document.py @@ -1,7 +1,6 @@ import re import warnings -from collections import OrderedDict from bson.dbref import DBRef import pymongo from pymongo.read_preferences import ReadPreference @@ -22,8 +21,7 @@ from mongoengine.queryset import (NotUniqueError, OperationError, __all__ = ('Document', 'EmbeddedDocument', 'DynamicDocument', 'DynamicEmbeddedDocument', 'OperationError', - 'InvalidCollectionError', 'NotUniqueError', 'MapReduceDocument', - 'OrderedDocument') + 'InvalidCollectionError', 'NotUniqueError', 'MapReduceDocument') def includes_cls(fields): @@ -1038,27 +1036,3 @@ class MapReduceDocument(object): self._key_object = self._document.objects.with_id(self.key) return self._key_object return self._key_object - - -class OrderedDocument(Document): - """A document that is almost same with Document except for returning - results in OrderedDict instead of dict. - """ - - # The __metaclass__ attribute is removed by 2to3 when running with Python3 - # my_metaclass is defined so that metaclass can be queried in Python 2 & 3 - my_metaclass = TopLevelDocumentMetaclass - __metaclass__ = TopLevelDocumentMetaclass - - @classmethod - def _get_collection(cls): - collection = super(OrderedDocument, cls)._get_collection() - - if IS_PYMONGO_3: - # returns collection object which is set OrderedDict class to be decoded from BSON document - from bson import CodecOptions - return collection.with_options(codec_options=CodecOptions(document_class=OrderedDict)) - else: - # set attribute to specify the class to be decoeded - cls.decoded_class = OrderedDict - return collection diff --git a/mongoengine/fields.py b/mongoengine/fields.py index 4b6a1a9b..70061e08 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -5,7 +5,6 @@ import re import time import uuid import warnings -from collections import OrderedDict from operator import itemgetter from bson import Binary, DBRef, ObjectId, SON @@ -50,7 +49,7 @@ __all__ = ( 'FileField', 'ImageGridFsProxy', 'ImproperlyConfigured', 'ImageField', 'GeoPointField', 'PointField', 'LineStringField', 'PolygonField', 'SequenceField', 'UUIDField', 'MultiPointField', 'MultiLineStringField', - 'MultiPolygonField', 'GeoJsonBaseField', 'OrderedDynamicField' + 'MultiPolygonField', 'GeoJsonBaseField' ) RECURSIVE_REFERENCE_CONSTANT = 'self' @@ -620,6 +619,14 @@ class DynamicField(BaseField): Used by :class:`~mongoengine.DynamicDocument` to handle dynamic data""" + def __init__(self, container_class=dict, *args, **kwargs): + self._container_cls = container_class + if not issubclass(self._container_cls, dict): + self.error('The class that is specified in `container_class` parameter ' + 'must be a subclass of `dict`.') + + super(DynamicField, self).__init__(*args, **kwargs) + def to_mongo(self, value, use_db_field=True, fields=None): """Convert a Python type to a MongoDB compatible type. """ @@ -645,7 +652,7 @@ class DynamicField(BaseField): is_list = True value = {k: v for k, v in enumerate(value)} - data = self._container_type() if hasattr(self, '_container_type') else {} + data = self._container_cls() for k, v in value.iteritems(): data[k] = self.to_mongo(v, use_db_field, fields) @@ -676,16 +683,6 @@ class DynamicField(BaseField): value.validate(clean=clean) -class OrderedDynamicField(DynamicField): - """A field that wraps DynamicField. This uses OrderedDict class - to guarantee to store data in the defined order instead of dict. - """ - - def __init__(self, *args, **kwargs): - super(OrderedDynamicField, self).__init__(*args, **kwargs) - self._container_type = OrderedDict - - class ListField(ComplexBaseField): """A list field that wraps a standard field, allowing multiple instances of the field to be used as a list in the database. diff --git a/mongoengine/queryset/base.py b/mongoengine/queryset/base.py index 4738f5f7..7e485686 100644 --- a/mongoengine/queryset/base.py +++ b/mongoengine/queryset/base.py @@ -1501,10 +1501,6 @@ class BaseQuerySet(object): cursor_args['read_preference'] = self._read_preference else: cursor_args['slave_okay'] = self._slave_okay - - # set decode format if needed - if hasattr(self._document, 'decoded_class'): - cursor_args['as_class'] = self._document.decoded_class else: fields_name = 'projection' # snapshot is not handled at all by PyMongo 3+ diff --git a/tests/fields/fields.py b/tests/fields/fields.py index 3516471f..b73eb0af 100644 --- a/tests/fields/fields.py +++ b/tests/fields/fields.py @@ -4500,39 +4500,31 @@ class EmbeddedDocumentListFieldTestCase(MongoDBTestCase): self.assertTrue(hasattr(CustomData.c_field, 'custom_data')) self.assertEqual(custom_data['a'], CustomData.c_field.custom_data['a']) - def test_ordered_dynamic_fields_class(self): + def test_dynamicfield_with_container_class(self): """ - Tests that OrderedDynamicFields interits features of the DynamicFields - and saves/retrieves data in order. + Tests that object can be stored in order by DynamicField class + with container_class parameter. """ - class Member(Document): - name = StringField() - age = IntField() + raw_data = [('d', 1), ('c', 2), ('b', 3), ('a', 4)] - class Team(OrderedDocument): - members = OrderedDynamicField() + class Doc(Document): + ordered_data = DynamicField(container_class=OrderedDict) + unordered_data = DynamicField() - Member.drop_collection() - Team.drop_collection() + Doc.drop_collection() - member_info = [ - ('Martin McFly', 17), - ('Emmett Brown', 65), - ('George McFly', 47) - ] - members = OrderedDict() - for name, age in member_info: - members[name] = Member(name=name, age=age) - members[name].save() + doc = Doc(ordered_data=OrderedDict(raw_data), + unordered_data=dict(raw_data)).save() - Team(members=members).save() + self.assertEqual(type(doc.ordered_data), OrderedDict) + self.assertEqual(type(doc.unordered_data), dict) + self.assertEqual([k for k,_ in doc.ordered_data.items()], ['d', 'c', 'b', 'a']) + self.assertNotEqual([k for k,_ in doc.unordered_data.items()], ['d', 'c', 'b', 'a']) - index = 0 - team = Team.objects.get() - for member in team.members: - print("%s == %s" % (member, member_info[index][0])) - self.assertEqual(member, member_info[index][0]) - index += 1 + def test_dynamicfield_with_wrong_container_class(self): + with self.assertRaises(ValidationError): + class DocWithInvalidField: + data = DynamicField(container_class=list) if __name__ == '__main__':