Merge pull request #2242 from bagerard/fix_dictfield_validation
Fix bug introduced in 0.19 related to DictField validation
This commit is contained in:
		@@ -8,7 +8,8 @@ Development
 | 
				
			|||||||
- (Fill this out as you fix issues and develop your features).
 | 
					- (Fill this out as you fix issues and develop your features).
 | 
				
			||||||
- Add Mongo 4.0 to Travis
 | 
					- Add Mongo 4.0 to Travis
 | 
				
			||||||
- Fixed a bug causing inaccurate query results, while combining ``__raw__`` and regular filters for the same field #2264
 | 
					- Fixed a bug causing inaccurate query results, while combining ``__raw__`` and regular filters for the same field #2264
 | 
				
			||||||
- Add support for the `elemMatch` projection operator in .fields (e.g BlogPost.objects.fields(elemMatch__comments="test")) #2267
 | 
					- Add support for the `elemMatch` projection operator in .fields() (e.g BlogPost.objects.fields(elemMatch__comments="test")) #2267
 | 
				
			||||||
 | 
					- DictField validate failed without default connection (bug introduced in 0.19.0) #2239
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Changes in 0.19.1
 | 
					Changes in 0.19.1
 | 
				
			||||||
=================
 | 
					=================
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1088,14 +1088,12 @@ class DictField(ComplexBaseField):
 | 
				
			|||||||
            msg = "Invalid dictionary key - documents must have only string keys"
 | 
					            msg = "Invalid dictionary key - documents must have only string keys"
 | 
				
			||||||
            self.error(msg)
 | 
					            self.error(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        curr_mongo_ver = get_mongodb_version()
 | 
					        # Following condition applies to MongoDB >= 3.6
 | 
				
			||||||
 | 
					        # older Mongo has stricter constraints but
 | 
				
			||||||
        if curr_mongo_ver < MONGODB_36 and key_has_dot_or_dollar(value):
 | 
					        # it will be rejected upon insertion anyway
 | 
				
			||||||
            self.error(
 | 
					        # Having a validation that depends on the MongoDB version
 | 
				
			||||||
                'Invalid dictionary key name - keys may not contain "."'
 | 
					        # is not straightforward as the field isn't aware of the connected Mongo
 | 
				
			||||||
                ' or startswith "$" characters'
 | 
					        if key_starts_with_dollar(value):
 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        elif curr_mongo_ver >= MONGODB_36 and key_starts_with_dollar(value):
 | 
					 | 
				
			||||||
            self.error(
 | 
					            self.error(
 | 
				
			||||||
                'Invalid dictionary key name - keys may not startswith "$" characters'
 | 
					                'Invalid dictionary key name - keys may not startswith "$" characters'
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@ MONGODB_36 = (3, 6)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_mongodb_version():
 | 
					def get_mongodb_version():
 | 
				
			||||||
    """Return the version of the connected mongoDB (first 2 digits)
 | 
					    """Return the version of the default connected mongoDB (first 2 digits)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    :return: tuple(int, int)
 | 
					    :return: tuple(int, int)
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
# -*- coding: utf-8 -*-
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
 | 
					from bson import InvalidDocument
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from mongoengine import *
 | 
					from mongoengine import *
 | 
				
			||||||
from mongoengine.base import BaseDict
 | 
					from mongoengine.base import BaseDict
 | 
				
			||||||
@@ -19,22 +20,24 @@ class TestDictField(MongoDBTestCase):
 | 
				
			|||||||
        post = BlogPost(info=info).save()
 | 
					        post = BlogPost(info=info).save()
 | 
				
			||||||
        assert get_as_pymongo(post) == {"_id": post.id, "info": info}
 | 
					        assert get_as_pymongo(post) == {"_id": post.id, "info": info}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_general_things(self):
 | 
					    def test_validate_invalid_type(self):
 | 
				
			||||||
        """Ensure that dict types work as expected."""
 | 
					        class BlogPost(Document):
 | 
				
			||||||
 | 
					            info = DictField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BlogPost.drop_collection()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        invalid_infos = ["my post", ["test", "test"], {1: "test"}]
 | 
				
			||||||
 | 
					        for invalid_info in invalid_infos:
 | 
				
			||||||
 | 
					            with pytest.raises(ValidationError):
 | 
				
			||||||
 | 
					                BlogPost(info=invalid_info).validate()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_keys_with_dots_or_dollars(self):
 | 
				
			||||||
        class BlogPost(Document):
 | 
					        class BlogPost(Document):
 | 
				
			||||||
            info = DictField()
 | 
					            info = DictField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        BlogPost.drop_collection()
 | 
					        BlogPost.drop_collection()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        post = BlogPost()
 | 
					        post = BlogPost()
 | 
				
			||||||
        post.info = "my post"
 | 
					 | 
				
			||||||
        with pytest.raises(ValidationError):
 | 
					 | 
				
			||||||
            post.validate()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        post.info = ["test", "test"]
 | 
					 | 
				
			||||||
        with pytest.raises(ValidationError):
 | 
					 | 
				
			||||||
            post.validate()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        post.info = {"$title": "test"}
 | 
					        post.info = {"$title": "test"}
 | 
				
			||||||
        with pytest.raises(ValidationError):
 | 
					        with pytest.raises(ValidationError):
 | 
				
			||||||
@@ -48,25 +51,34 @@ class TestDictField(MongoDBTestCase):
 | 
				
			|||||||
        with pytest.raises(ValidationError):
 | 
					        with pytest.raises(ValidationError):
 | 
				
			||||||
            post.validate()
 | 
					            post.validate()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        post.info = {1: "test"}
 | 
					 | 
				
			||||||
        with pytest.raises(ValidationError):
 | 
					 | 
				
			||||||
            post.validate()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        post.info = {"nested": {"the.title": "test"}}
 | 
					        post.info = {"nested": {"the.title": "test"}}
 | 
				
			||||||
        if get_mongodb_version() < MONGODB_36:
 | 
					        if get_mongodb_version() < MONGODB_36:
 | 
				
			||||||
            with pytest.raises(ValidationError):
 | 
					            # MongoDB < 3.6 rejects dots
 | 
				
			||||||
                post.validate()
 | 
					            # To avoid checking the mongodb version from the DictField class
 | 
				
			||||||
 | 
					            # we rely on MongoDB to reject the data during the save
 | 
				
			||||||
 | 
					            post.validate()
 | 
				
			||||||
 | 
					            with pytest.raises(InvalidDocument):
 | 
				
			||||||
 | 
					                post.save()
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            post.validate()
 | 
					            post.validate()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        post.info = {"dollar_and_dot": {"te$st.test": "test"}}
 | 
					        post.info = {"dollar_and_dot": {"te$st.test": "test"}}
 | 
				
			||||||
        if get_mongodb_version() < MONGODB_36:
 | 
					        if get_mongodb_version() < MONGODB_36:
 | 
				
			||||||
            with pytest.raises(ValidationError):
 | 
					            post.validate()
 | 
				
			||||||
                post.validate()
 | 
					            with pytest.raises(InvalidDocument):
 | 
				
			||||||
 | 
					                post.save()
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            post.validate()
 | 
					            post.validate()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        post.info = {"title": "test"}
 | 
					    def test_general_things(self):
 | 
				
			||||||
 | 
					        """Ensure that dict types work as expected."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class BlogPost(Document):
 | 
				
			||||||
 | 
					            info = DictField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BlogPost.drop_collection()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        post = BlogPost(info={"title": "test"})
 | 
				
			||||||
        post.save()
 | 
					        post.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        post = BlogPost()
 | 
					        post = BlogPost()
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user