Merge pull request #957 from noirbizarre/metastrict

Allow to loads undeclared field with meta attribute (fix #934)
This commit is contained in:
Omer Katz 2015-04-29 19:32:26 +03:00
commit 3f14958741
6 changed files with 145 additions and 6 deletions

View File

@ -34,6 +34,9 @@ Documents
.. autoclass:: mongoengine.ValidationError .. autoclass:: mongoengine.ValidationError
:members: :members:
.. autoclass:: mongoengine.FieldDoesNotExist
Context Managers Context Managers
================ ================

View File

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

View File

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

View File

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

View File

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

View File

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