diff --git a/mongoengine/base.py b/mongoengine/base.py index 9ac35607..99285d6b 100644 --- a/mongoengine/base.py +++ b/mongoengine/base.py @@ -344,8 +344,12 @@ class BaseDocument(object): return None cls = subclasses[class_name] + present_fields = data.keys() + for field_name, field in cls._fields.items(): if field.name in data: data[field_name] = field.to_python(data[field.name]) - return cls(**data) + obj = cls(**data) + obj._present_fields = present_fields + return obj diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index 60f3f8df..f47f5ac0 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -112,6 +112,7 @@ class QuerySet(object): self._accessed_collection = False self._query = {} self._where_clauses = [] + self._field_subset = [] # If inheritance is allowed, only return instances and instances of # subclasses of the class being used @@ -188,7 +189,12 @@ class QuerySet(object): @property def _cursor(self): if not self._cursor_obj: - self._cursor_obj = self._collection.find(self._query) + query_kwargs = {} + if self._field_subset: + # load only a subset of fields + query_kwargs['fields'] = self._field_subset + self._cursor_obj = self._collection.find(self._query, **query_kwargs) + # Apply where clauses to cursor for js in self._where_clauses: self._cursor_obj.where(js) @@ -334,6 +340,14 @@ class QuerySet(object): elif isinstance(key, int): return self._document._from_son(self._cursor[key]) + def only(self, *fields): + """Allow only a subset of fields to be loaded. Invalid fields + are silently ignored. + """ + fields = list(fields) + self._field_subset = fields + return self + def order_by(self, *keys): """Order the :class:`~mongoengine.queryset.QuerySet` by the keys. The order may be specified by prepending each of the keys by a + or a -. diff --git a/tests/queryset.py b/tests/queryset.py index 93dc0747..763da738 100644 --- a/tests/queryset.py +++ b/tests/queryset.py @@ -171,6 +171,30 @@ class QuerySetTest(unittest.TestCase): BlogPost.drop_collection() + def test_field_subsets(self): + """Ensure that a call to ``only`` loads only selected fields. + """ + + class DinerReview(Document): + title = StringField() + abstract = StringField() + content = StringField() + + review = DinerReview(title="Lorraine's Diner") + review.abstract = "Dirty dishes, great food." + review.content = """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Mauris eu felis risus, eget congue ante. Mauris consectetur + dignissim velit, quis dictum risus tincidunt ac. + Phasellus condimentum imperdiet laoreet. + """ + review.save() + + review = DinerReview.objects.only("title").first() + self.assertEqual(review.content, None) + + DinerReview.drop_collection() + def test_ordering(self): """Ensure default ordering is applied and can be overridden. """