From 89785da1c5f0f3166fc6cf5cf03dcc6e4136bf6d Mon Sep 17 00:00:00 2001 From: Stefan Wojcik Date: Mon, 16 Sep 2013 23:50:13 -0700 Subject: [PATCH] fix validation for a nested DictField --- mongoengine/fields.py | 19 +++++++++++++++++-- tests/fields/fields.py | 6 ++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/mongoengine/fields.py b/mongoengine/fields.py index 419f2ef7..2635b817 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -735,6 +735,21 @@ class SortedListField(ListField): reverse=self._order_reverse) return sorted(value, reverse=self._order_reverse) +def key_not_string(d): + """ Helper function to recursively determine if any key in a dictionary is + not a string. + """ + for k, v in d.items(): + if not isinstance(k, basestring) or (isinstance(v, dict) and key_not_string(v)): + return True + +def key_has_dot_or_dollar(d): + """ Helper function to recursively determine if any key in a dictionary + contains a dot or a dollar sign. + """ + for k, v in d.items(): + if ('.' in k or '$' in k) or (isinstance(v, dict) and key_has_dot_or_dollar(v)): + return True class DictField(ComplexBaseField): """A dictionary field that wraps a standard Python dictionary. This is @@ -761,11 +776,11 @@ class DictField(ComplexBaseField): if not isinstance(value, dict): self.error('Only dictionaries may be used in a DictField') - if any(k for k in value.keys() if not isinstance(k, basestring)): + if key_not_string(value): msg = ("Invalid dictionary key - documents must " "have only string keys") self.error(msg) - if any(('.' in k or '$' in k) for k in value.keys()): + if key_has_dot_or_dollar(value): self.error('Invalid dictionary key name - keys may not contain "."' ' or "$" characters') super(DictField, self).validate(value) diff --git a/tests/fields/fields.py b/tests/fields/fields.py index 8791781c..a9db8619 100644 --- a/tests/fields/fields.py +++ b/tests/fields/fields.py @@ -1109,9 +1109,15 @@ class FieldTest(unittest.TestCase): post.info = {'$title': 'test'} self.assertRaises(ValidationError, post.validate) + post.info = {'nested': {'$title': 'test'}} + self.assertRaises(ValidationError, post.validate) + post.info = {'the.title': 'test'} self.assertRaises(ValidationError, post.validate) + post.info = {'nested': {'the.title': 'test'}} + self.assertRaises(ValidationError, post.validate) + post.info = {1: 'test'} self.assertRaises(ValidationError, post.validate)