Skip to content

Fix bug introduced in 0.19 related to DictField validation #2242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
=================
Expand Down
14 changes: 6 additions & 8 deletions mongoengine/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -1088,14 +1088,12 @@ def validate(self, value):
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'
)
Expand Down
2 changes: 1 addition & 1 deletion mongoengine/mongodb_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@


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)
"""
Expand Down
48 changes: 30 additions & 18 deletions tests/fields/test_dict_field.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import pytest
from bson import InvalidDocument

from mongoengine import *
from mongoengine.base import BaseDict
Expand All @@ -19,22 +20,24 @@ class BlogPost(Document):
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):
Expand All @@ -48,25 +51,34 @@ class BlogPost(Document):
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):
post.validate()
# 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()
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()
Expand Down