From e00891997856b9bc290e94263d1d1ea9a5ed353b Mon Sep 17 00:00:00 2001 From: DavidBord Date: Sun, 3 Aug 2014 17:03:24 +0300 Subject: [PATCH] fix-#399: Not overriding default values when loading a subset of fields --- mongoengine/base/document.py | 11 ++++++++--- mongoengine/queryset/base.py | 21 ++++++++++++--------- tests/document/instance.py | 15 +++++++++++++++ 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/mongoengine/base/document.py b/mongoengine/base/document.py index 7509fffe..6f17202b 100644 --- a/mongoengine/base/document.py +++ b/mongoengine/base/document.py @@ -55,6 +55,10 @@ class BaseDocument(object): "Multiple values for keyword argument '" + name + "'") values[name] = value __auto_convert = values.pop("__auto_convert", True) + + # 399: set default values only to fields loaded from DB + __only_fields = set(values.pop("__only_fields", values)) + signals.pre_init.send(self.__class__, document=self, values=values) if self.STRICT and not self._dynamic: @@ -69,7 +73,7 @@ class BaseDocument(object): # Assign default values to instance for key, field in self._fields.iteritems(): - if self._db_field_map.get(key, key) in values: + if self._db_field_map.get(key, key) in __only_fields: continue value = getattr(self, key, None) setattr(self, key, value) @@ -610,7 +614,7 @@ class BaseDocument(object): return cls._meta.get('collection', None) @classmethod - def _from_son(cls, son, _auto_dereference=True): + def _from_son(cls, son, _auto_dereference=True, only_fields=[]): """Create an instance of a Document (subclass) from a PyMongo SON. """ @@ -658,10 +662,11 @@ class BaseDocument(object): if cls.STRICT: data = dict((k, v) for k, v in data.iteritems() if k in cls._fields) - obj = cls(__auto_convert=False, _created=False, **data) + obj = cls(__auto_convert=False, _created=False, __only_fields=only_fields, **data) obj._changed_fields = changed_fields if not _auto_dereference: obj._fields = fields + return obj @classmethod diff --git a/mongoengine/queryset/base.py b/mongoengine/queryset/base.py index f64b0075..2b5c93d5 100644 --- a/mongoengine/queryset/base.py +++ b/mongoengine/queryset/base.py @@ -81,6 +81,7 @@ class BaseQuerySet(object): self._limit = None self._skip = None self._hint = -1 # Using -1 as None is a valid value for hint + self.only_fields = [] def __call__(self, q_obj=None, class_check=True, slave_okay=False, read_preference=None, **query): @@ -151,12 +152,13 @@ class BaseQuerySet(object): if queryset._scalar: return queryset._get_scalar( queryset._document._from_son(queryset._cursor[key], - _auto_dereference=self._auto_dereference)) + _auto_dereference=self._auto_dereference, + only_fields=self.only_fields)) if queryset._as_pymongo: return queryset._get_as_pymongo(queryset._cursor[key]) return queryset._document._from_son(queryset._cursor[key], - _auto_dereference=self._auto_dereference) + _auto_dereference=self._auto_dereference, only_fields=self.only_fields) raise AttributeError def __iter__(self): @@ -570,10 +572,10 @@ class BaseQuerySet(object): if full_response: if result["value"] is not None: - result["value"] = self._document._from_son(result["value"]) + result["value"] = self._document._from_son(result["value"], only_fields=self.only_fields) else: if result is not None: - result = self._document._from_son(result) + result = self._document._from_son(result, only_fields=self.only_fields) return result @@ -608,13 +610,13 @@ class BaseQuerySet(object): if self._scalar: for doc in docs: doc_map[doc['_id']] = self._get_scalar( - self._document._from_son(doc)) + self._document._from_son(doc, only_fields=self.only_fields)) elif self._as_pymongo: for doc in docs: doc_map[doc['_id']] = self._get_as_pymongo(doc) else: for doc in docs: - doc_map[doc['_id']] = self._document._from_son(doc) + doc_map[doc['_id']] = self._document._from_son(doc, only_fields=self.only_fields) return doc_map @@ -667,7 +669,7 @@ class BaseQuerySet(object): '_timeout', '_class_check', '_slave_okay', '_read_preference', '_iter', '_scalar', '_as_pymongo', '_as_pymongo_coerce', '_limit', '_skip', '_hint', '_auto_dereference', - '_search_text', '_include_text_scores') + '_search_text', '_include_text_scores', 'only_fields') for prop in copy_props: val = getattr(self, prop) @@ -785,6 +787,7 @@ class BaseQuerySet(object): .. versionchanged:: 0.5 - Added subfield support """ fields = dict([(f, QueryFieldList.ONLY) for f in fields]) + self.only_fields = fields.keys() return self.fields(True, **fields) def exclude(self, *fields): @@ -972,7 +975,7 @@ class BaseQuerySet(object): def from_json(self, json_data): """Converts json data to unsaved objects""" son_data = json_util.loads(json_data) - return [self._document._from_son(data) for data in son_data] + return [self._document._from_son(data, only_fields=self.only_fields) for data in son_data] def aggregate(self, *pipeline, **kwargs): """ @@ -1324,7 +1327,7 @@ class BaseQuerySet(object): if self._as_pymongo: return self._get_as_pymongo(raw_doc) doc = self._document._from_son(raw_doc, - _auto_dereference=self._auto_dereference) + _auto_dereference=self._auto_dereference, only_fields=self.only_fields) if self._scalar: return self._get_scalar(doc) diff --git a/tests/document/instance.py b/tests/document/instance.py index 0903e3bb..a639fd79 100644 --- a/tests/document/instance.py +++ b/tests/document/instance.py @@ -2618,5 +2618,20 @@ class InstanceTest(unittest.TestCase): self.assertTrue(obj3 != dbref2) self.assertTrue(dbref2 != obj3) + def test_default_values(self): + class Person(Document): + created_on = DateTimeField(default=lambda: datetime.utcnow()) + name = StringField() + + p = Person(name='alon') + p.save() + orig_created_on = Person.objects().only('created_on')[0].created_on + + p2 = Person.objects().only('name')[0] + p2.name = 'alon2' + p2.save() + p3 = Person.objects().only('created_on')[0] + self.assertEquals(orig_created_on, p3.created_on) + if __name__ == '__main__': unittest.main()