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).
 | 
			
		||||
- Add Mongo 4.0 to Travis
 | 
			
		||||
- 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
 | 
			
		||||
=================
 | 
			
		||||
 
 | 
			
		||||
@@ -1088,14 +1088,12 @@ class DictField(ComplexBaseField):
 | 
			
		||||
            msg = "Invalid dictionary key - documents must have only string keys"
 | 
			
		||||
            self.error(msg)
 | 
			
		||||
 | 
			
		||||
        curr_mongo_ver = get_mongodb_version()
 | 
			
		||||
 | 
			
		||||
        if curr_mongo_ver < MONGODB_36 and key_has_dot_or_dollar(value):
 | 
			
		||||
            self.error(
 | 
			
		||||
                'Invalid dictionary key name - keys may not contain "."'
 | 
			
		||||
                ' or startswith "$" characters'
 | 
			
		||||
            )
 | 
			
		||||
        elif curr_mongo_ver >= MONGODB_36 and key_starts_with_dollar(value):
 | 
			
		||||
        # Following condition applies to MongoDB >= 3.6
 | 
			
		||||
        # older Mongo has stricter constraints but
 | 
			
		||||
        # it will be rejected upon insertion anyway
 | 
			
		||||
        # Having a validation that depends on the MongoDB version
 | 
			
		||||
        # is not straightforward as the field isn't aware of the connected Mongo
 | 
			
		||||
        if key_starts_with_dollar(value):
 | 
			
		||||
            self.error(
 | 
			
		||||
                'Invalid dictionary key name - keys may not startswith "$" characters'
 | 
			
		||||
            )
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ MONGODB_36 = (3, 6)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
    """
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
import pytest
 | 
			
		||||
from bson import InvalidDocument
 | 
			
		||||
 | 
			
		||||
from mongoengine import *
 | 
			
		||||
from mongoengine.base import BaseDict
 | 
			
		||||
@@ -19,22 +20,24 @@ class TestDictField(MongoDBTestCase):
 | 
			
		||||
        post = BlogPost(info=info).save()
 | 
			
		||||
        assert get_as_pymongo(post) == {"_id": post.id, "info": info}
 | 
			
		||||
 | 
			
		||||
    def test_general_things(self):
 | 
			
		||||
        """Ensure that dict types work as expected."""
 | 
			
		||||
    def test_validate_invalid_type(self):
 | 
			
		||||
        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):
 | 
			
		||||
            info = DictField()
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
        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"}
 | 
			
		||||
        with pytest.raises(ValidationError):
 | 
			
		||||
@@ -48,25 +51,34 @@ class TestDictField(MongoDBTestCase):
 | 
			
		||||
        with pytest.raises(ValidationError):
 | 
			
		||||
            post.validate()
 | 
			
		||||
 | 
			
		||||
        post.info = {1: "test"}
 | 
			
		||||
        with pytest.raises(ValidationError):
 | 
			
		||||
            post.validate()
 | 
			
		||||
 | 
			
		||||
        post.info = {"nested": {"the.title": "test"}}
 | 
			
		||||
        if get_mongodb_version() < MONGODB_36:
 | 
			
		||||
            with pytest.raises(ValidationError):
 | 
			
		||||
            # MongoDB < 3.6 rejects dots
 | 
			
		||||
            # 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:
 | 
			
		||||
            post.validate()
 | 
			
		||||
 | 
			
		||||
        post.info = {"dollar_and_dot": {"te$st.test": "test"}}
 | 
			
		||||
        if get_mongodb_version() < MONGODB_36:
 | 
			
		||||
            with pytest.raises(ValidationError):
 | 
			
		||||
            post.validate()
 | 
			
		||||
            with pytest.raises(InvalidDocument):
 | 
			
		||||
                post.save()
 | 
			
		||||
        else:
 | 
			
		||||
            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 = BlogPost()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user