Skip to content
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: 3 additions & 0 deletions docs/apireference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ Documents
.. autoclass:: mongoengine.ValidationError
:members:

.. autoclass:: mongoengine.FieldDoesNotExist


Context Managers
================

Expand Down
6 changes: 3 additions & 3 deletions mongoengine/base/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ def __init__(self, *args, **values):

signals.pre_init.send(self.__class__, document=self, values=values)

# Check if there are undefined fields supplied, if so raise an
# Exception.
if not self._dynamic:
# Check if there are undefined fields supplied to the constructor,
# if so raise an Exception.
if not self._dynamic and (self._meta.get('strict', True) or _created):
for var in values.keys():
if var not in self._fields.keys() + ['id', 'pk', '_cls', '_text_score']:
msg = (
Expand Down
5 changes: 5 additions & 0 deletions mongoengine/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ class Document(BaseDocument):
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
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
Expand Down
9 changes: 8 additions & 1 deletion mongoengine/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,14 @@ class NotUniqueError(OperationError):


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):
Expand Down
111 changes: 110 additions & 1 deletion tests/document/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

from mongoengine import *
from mongoengine.errors import (NotRegistered, InvalidDocumentError,
InvalidQueryError, NotUniqueError)
InvalidQueryError, NotUniqueError,
FieldDoesNotExist)
from mongoengine.queryset import NULLIFY, Q
from mongoengine.connection import get_db
from mongoengine.base import get_document
Expand Down Expand Up @@ -2467,6 +2468,114 @@ class Group(Document):
group = Group.objects.first()
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):

class Embedded(DynamicEmbeddedDocument):
Expand Down
17 changes: 16 additions & 1 deletion tests/fields/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -3160,7 +3160,7 @@ class Doc(Document):

def test_undefined_field_exception(self):
"""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):
Expand All @@ -3171,6 +3171,21 @@ def 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):

Expand Down