To achive storing object data in order with minimum implementation, I
changed followings. - added optional parameter `container_class` which enables to choose intermediate class at encoding Python data, instead of additional field class. - removed OrderedDocument class because the equivalent feature could be implemented by the outside of Mongoengine.
This commit is contained in:
parent
e32a9777d7
commit
5957dc72eb
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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+
|
||||
|
@ -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__':
|
||||
|
Loading…
x
Reference in New Issue
Block a user