From 9f4b04ea0f4d4a6d15a3f59f674d11f073ebe2a1 Mon Sep 17 00:00:00 2001 From: Manuel Jeckelmann Date: Thu, 22 Dec 2016 19:19:18 +0100 Subject: [PATCH] Fix querying an embedded document field by an invalid value (#1440) --- mongoengine/base/document.py | 6 ++++-- mongoengine/fields.py | 8 ++++++-- tests/document/instance.py | 4 ++++ tests/queryset/queryset.py | 18 ++++++++++++++++-- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/mongoengine/base/document.py b/mongoengine/base/document.py index 03dc7562..03536d6f 100644 --- a/mongoengine/base/document.py +++ b/mongoengine/base/document.py @@ -16,8 +16,7 @@ from mongoengine.base.datastructures import (BaseDict, BaseList, SemiStrictDict, StrictDict) from mongoengine.base.fields import ComplexBaseField from mongoengine.common import _import_class -from mongoengine.errors import (FieldDoesNotExist, InvalidDocumentError, - LookUpError, OperationError, ValidationError) +from mongoengine.errors import (FieldDoesNotExist, InvalidDocumentError, LookUpError, OperationError, ValidationError) __all__ = ('BaseDocument',) @@ -675,6 +674,9 @@ class BaseDocument(object): if not only_fields: only_fields = [] + if son and not isinstance(son, dict): + raise ValueError("The source SON object needs to be of type 'dict'") + # Get the class name from the document, falling back to the given # class if unavailable class_name = son.get('_cls', cls._class_name) diff --git a/mongoengine/fields.py b/mongoengine/fields.py index d812a762..3991ef5c 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -28,7 +28,7 @@ from mongoengine.base import (BaseDocument, BaseField, ComplexBaseField, GeoJsonBaseField, ObjectIdField, get_document) from mongoengine.connection import DEFAULT_CONNECTION_NAME, get_db from mongoengine.document import Document, EmbeddedDocument -from mongoengine.errors import DoesNotExist, ValidationError +from mongoengine.errors import DoesNotExist, InvalidQueryError, ValidationError from mongoengine.python_support import StringIO from mongoengine.queryset import DO_NOTHING, QuerySet @@ -566,7 +566,11 @@ class EmbeddedDocumentField(BaseField): def prepare_query_value(self, op, value): if value is not None and not isinstance(value, self.document_type): - value = self.document_type._from_son(value) + try: + value = self.document_type._from_son(value) + except ValueError: + raise InvalidQueryError("Querying the embedded document '%s' failed, due to an invalid query value" % + (self.document_type._class_name,)) super(EmbeddedDocumentField, self).prepare_query_value(op, value) return self.to_mongo(value) diff --git a/tests/document/instance.py b/tests/document/instance.py index b92bafa9..d961f034 100644 --- a/tests/document/instance.py +++ b/tests/document/instance.py @@ -1860,6 +1860,10 @@ class InstanceTest(unittest.TestCase): 'occurs': {"hello": None} }) + # Tests for issue #1438: https://github.com/MongoEngine/mongoengine/issues/1438 + with self.assertRaises(ValueError): + Word._from_son('this is not a valid SON dict') + def test_reverse_delete_rule_cascade_and_nullify(self): """Ensure that a referenced document is also deleted upon deletion. """ diff --git a/tests/queryset/queryset.py b/tests/queryset/queryset.py index 11ce0a27..a15807a5 100644 --- a/tests/queryset/queryset.py +++ b/tests/queryset/queryset.py @@ -1266,7 +1266,7 @@ class QuerySetTest(unittest.TestCase): def test_find_embedded(self): """Ensure that an embedded document is properly returned from - a query. + different manners of querying. """ class User(EmbeddedDocument): name = StringField() @@ -1277,8 +1277,9 @@ class QuerySetTest(unittest.TestCase): BlogPost.drop_collection() + user = User(name='Test User') BlogPost.objects.create( - author=User(name='Test User'), + author=user, content='Had a good coffee today...' ) @@ -1286,6 +1287,19 @@ class QuerySetTest(unittest.TestCase): self.assertTrue(isinstance(result.author, User)) self.assertEqual(result.author.name, 'Test User') + result = BlogPost.objects.get(author__name=user.name) + self.assertTrue(isinstance(result.author, User)) + self.assertEqual(result.author.name, 'Test User') + + result = BlogPost.objects.get(author={'name': user.name}) + self.assertTrue(isinstance(result.author, User)) + self.assertEqual(result.author.name, 'Test User') + + # Fails, since the string is not a type that is able to represent the + # author's document structure (should be dict) + with self.assertRaises(InvalidQueryError): + BlogPost.objects.get(author=user.name) + def test_find_empty_embedded(self): """Ensure that you can save and find an empty embedded document.""" class User(EmbeddedDocument):