Merge pull request #957 from noirbizarre/metastrict
Allow to loads undeclared field with meta attribute (fix #934)
This commit is contained in:
		| @@ -34,6 +34,9 @@ Documents | |||||||
| .. autoclass:: mongoengine.ValidationError | .. autoclass:: mongoengine.ValidationError | ||||||
|   :members: |   :members: | ||||||
|  |  | ||||||
|  | .. autoclass:: mongoengine.FieldDoesNotExist | ||||||
|  |  | ||||||
|  |  | ||||||
| Context Managers | Context Managers | ||||||
| ================ | ================ | ||||||
|  |  | ||||||
|   | |||||||
| @@ -70,9 +70,9 @@ class BaseDocument(object): | |||||||
|  |  | ||||||
|         signals.pre_init.send(self.__class__, document=self, values=values) |         signals.pre_init.send(self.__class__, document=self, values=values) | ||||||
|  |  | ||||||
|         # Check if there are undefined fields supplied, if so raise an |         # Check if there are undefined fields supplied to the constructor, | ||||||
|         # Exception. |         # if so raise an Exception. | ||||||
|         if not self._dynamic: |         if not self._dynamic and (self._meta.get('strict', True) or _created): | ||||||
|             for var in values.keys(): |             for var in values.keys(): | ||||||
|                 if var not in self._fields.keys() + ['id', 'pk', '_cls', '_text_score']: |                 if var not in self._fields.keys() + ['id', 'pk', '_cls', '_text_score']: | ||||||
|                     msg = ( |                     msg = ( | ||||||
|   | |||||||
| @@ -135,6 +135,11 @@ class Document(BaseDocument): | |||||||
|     doesn't contain a list) if allow_inheritance is True. This can be |     doesn't contain a list) if allow_inheritance is True. This can be | ||||||
|     disabled by either setting cls to False on the specific index or |     disabled by either setting cls to False on the specific index or | ||||||
|     by setting index_cls to False on the meta dictionary for the document. |     by setting index_cls to False on the meta dictionary for the document. | ||||||
|  |  | ||||||
|  |     By default, any extra attribute existing in stored data but not declared | ||||||
|  |     in your model will raise a :class:`~mongoengine.FieldDoesNotExist` error. | ||||||
|  |     This can be disabled by setting :attr:`strict` to ``False`` | ||||||
|  |     in the :attr:`meta` dictionnary. | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     # The __metaclass__ attribute is removed by 2to3 when running with Python3 |     # The __metaclass__ attribute is removed by 2to3 when running with Python3 | ||||||
|   | |||||||
| @@ -42,7 +42,14 @@ class NotUniqueError(OperationError): | |||||||
|  |  | ||||||
|  |  | ||||||
| class FieldDoesNotExist(Exception): | class FieldDoesNotExist(Exception): | ||||||
|     pass |     """Raised when trying to set a field | ||||||
|  |     not declared in a :class:`~mongoengine.Document` | ||||||
|  |     or an :class:`~mongoengine.EmbeddedDocument`. | ||||||
|  |  | ||||||
|  |     To avoid this behavior on data loading, | ||||||
|  |     you should the :attr:`strict` to ``False`` | ||||||
|  |     in the :attr:`meta` dictionnary. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |  | ||||||
| class ValidationError(AssertionError): | class ValidationError(AssertionError): | ||||||
|   | |||||||
| @@ -16,7 +16,8 @@ from tests.fixtures import (PickleEmbedded, PickleTest, PickleSignalsTest, | |||||||
|  |  | ||||||
| from mongoengine import * | from mongoengine import * | ||||||
| from mongoengine.errors import (NotRegistered, InvalidDocumentError, | from mongoengine.errors import (NotRegistered, InvalidDocumentError, | ||||||
|                                 InvalidQueryError, NotUniqueError) |                                 InvalidQueryError, NotUniqueError, | ||||||
|  |                                 FieldDoesNotExist) | ||||||
| from mongoengine.queryset import NULLIFY, Q | from mongoengine.queryset import NULLIFY, Q | ||||||
| from mongoengine.connection import get_db | from mongoengine.connection import get_db | ||||||
| from mongoengine.base import get_document | from mongoengine.base import get_document | ||||||
| @@ -2467,6 +2468,114 @@ class InstanceTest(unittest.TestCase): | |||||||
|         group = Group.objects.first() |         group = Group.objects.first() | ||||||
|         self.assertEqual("hello - default", group.name) |         self.assertEqual("hello - default", group.name) | ||||||
|  |  | ||||||
|  |     def test_load_undefined_fields(self): | ||||||
|  |         class User(Document): | ||||||
|  |             name = StringField() | ||||||
|  |  | ||||||
|  |         User.drop_collection() | ||||||
|  |  | ||||||
|  |         User._get_collection().save({ | ||||||
|  |             'name': 'John', | ||||||
|  |             'foo': 'Bar', | ||||||
|  |             'data': [1, 2, 3] | ||||||
|  |         }) | ||||||
|  |  | ||||||
|  |         self.assertRaises(FieldDoesNotExist, User.objects.first) | ||||||
|  |  | ||||||
|  |     def test_load_undefined_fields_with_strict_false(self): | ||||||
|  |         class User(Document): | ||||||
|  |             name = StringField() | ||||||
|  |  | ||||||
|  |             meta = {'strict': False} | ||||||
|  |  | ||||||
|  |         User.drop_collection() | ||||||
|  |  | ||||||
|  |         User._get_collection().save({ | ||||||
|  |             'name': 'John', | ||||||
|  |             'foo': 'Bar', | ||||||
|  |             'data': [1, 2, 3] | ||||||
|  |         }) | ||||||
|  |  | ||||||
|  |         user = User.objects.first() | ||||||
|  |         self.assertEqual(user.name, 'John') | ||||||
|  |         self.assertFalse(hasattr(user, 'foo')) | ||||||
|  |         self.assertEqual(user._data['foo'], 'Bar') | ||||||
|  |         self.assertFalse(hasattr(user, 'data')) | ||||||
|  |         self.assertEqual(user._data['data'], [1, 2, 3]) | ||||||
|  |  | ||||||
|  |     def test_load_undefined_fields_on_embedded_document(self): | ||||||
|  |         class Thing(EmbeddedDocument): | ||||||
|  |             name = StringField() | ||||||
|  |  | ||||||
|  |         class User(Document): | ||||||
|  |             name = StringField() | ||||||
|  |             thing = EmbeddedDocumentField(Thing) | ||||||
|  |  | ||||||
|  |         User.drop_collection() | ||||||
|  |  | ||||||
|  |         User._get_collection().save({ | ||||||
|  |             'name': 'John', | ||||||
|  |             'thing': { | ||||||
|  |                 'name': 'My thing', | ||||||
|  |                 'foo': 'Bar', | ||||||
|  |                 'data': [1, 2, 3] | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |  | ||||||
|  |         self.assertRaises(FieldDoesNotExist, User.objects.first) | ||||||
|  |  | ||||||
|  |     def test_load_undefined_fields_on_embedded_document_with_strict_false_on_doc(self): | ||||||
|  |         class Thing(EmbeddedDocument): | ||||||
|  |             name = StringField() | ||||||
|  |  | ||||||
|  |         class User(Document): | ||||||
|  |             name = StringField() | ||||||
|  |             thing = EmbeddedDocumentField(Thing) | ||||||
|  |  | ||||||
|  |             meta = {'strict': False} | ||||||
|  |  | ||||||
|  |         User.drop_collection() | ||||||
|  |  | ||||||
|  |         User._get_collection().save({ | ||||||
|  |             'name': 'John', | ||||||
|  |             'thing': { | ||||||
|  |                 'name': 'My thing', | ||||||
|  |                 'foo': 'Bar', | ||||||
|  |                 'data': [1, 2, 3] | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |  | ||||||
|  |         self.assertRaises(FieldDoesNotExist, User.objects.first) | ||||||
|  |  | ||||||
|  |     def test_load_undefined_fields_on_embedded_document_with_strict_false(self): | ||||||
|  |         class Thing(EmbeddedDocument): | ||||||
|  |             name = StringField() | ||||||
|  |  | ||||||
|  |             meta = {'strict': False} | ||||||
|  |  | ||||||
|  |         class User(Document): | ||||||
|  |             name = StringField() | ||||||
|  |             thing = EmbeddedDocumentField(Thing) | ||||||
|  |  | ||||||
|  |         User.drop_collection() | ||||||
|  |  | ||||||
|  |         User._get_collection().save({ | ||||||
|  |             'name': 'John', | ||||||
|  |             'thing': { | ||||||
|  |                 'name': 'My thing', | ||||||
|  |                 'foo': 'Bar', | ||||||
|  |                 'data': [1, 2, 3] | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |  | ||||||
|  |         user = User.objects.first() | ||||||
|  |         self.assertEqual(user.name, 'John') | ||||||
|  |         self.assertEqual(user.thing.name, 'My thing') | ||||||
|  |         self.assertFalse(hasattr(user.thing, 'foo')) | ||||||
|  |         self.assertEqual(user.thing._data['foo'], 'Bar') | ||||||
|  |         self.assertFalse(hasattr(user.thing, 'data')) | ||||||
|  |         self.assertEqual(user.thing._data['data'], [1, 2, 3]) | ||||||
|  |  | ||||||
|     def test_spaces_in_keys(self): |     def test_spaces_in_keys(self): | ||||||
|  |  | ||||||
|         class Embedded(DynamicEmbeddedDocument): |         class Embedded(DynamicEmbeddedDocument): | ||||||
|   | |||||||
| @@ -3195,7 +3195,7 @@ class FieldTest(unittest.TestCase): | |||||||
|  |  | ||||||
|     def test_undefined_field_exception(self): |     def test_undefined_field_exception(self): | ||||||
|         """Tests if a `FieldDoesNotExist` exception is raised when trying to |         """Tests if a `FieldDoesNotExist` exception is raised when trying to | ||||||
|         set a value to a field that's not defined. |         instanciate a document with a field that's not defined. | ||||||
|         """ |         """ | ||||||
|  |  | ||||||
|         class Doc(Document): |         class Doc(Document): | ||||||
| @@ -3206,6 +3206,21 @@ class FieldTest(unittest.TestCase): | |||||||
|  |  | ||||||
|         self.assertRaises(FieldDoesNotExist, test) |         self.assertRaises(FieldDoesNotExist, test) | ||||||
|  |  | ||||||
|  |     def test_undefined_field_exception_with_strict(self): | ||||||
|  |         """Tests if a `FieldDoesNotExist` exception is raised when trying to | ||||||
|  |         instanciate a document with a field that's not defined, | ||||||
|  |         even when strict is set to False. | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         class Doc(Document): | ||||||
|  |             foo = StringField(db_field='f') | ||||||
|  |             meta = {'strict': False} | ||||||
|  |  | ||||||
|  |         def test(): | ||||||
|  |             Doc(bar='test') | ||||||
|  |  | ||||||
|  |         self.assertRaises(FieldDoesNotExist, test) | ||||||
|  |  | ||||||
|  |  | ||||||
| class EmbeddedDocumentListFieldTestCase(unittest.TestCase): | class EmbeddedDocumentListFieldTestCase(unittest.TestCase): | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user