Merge pull request #2242 from bagerard/fix_dictfield_validation

Fix bug introduced in 0.19 related to DictField validation
This commit is contained in:
Bastien Gérard 2020-03-17 20:26:54 +01:00 committed by GitHub
commit 00ae6298d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 39 additions and 28 deletions

View File

@ -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
================= =================

View File

@ -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'
) )

View File

@ -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)
""" """

View File

@ -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()