diff --git a/docs/apireference.rst b/docs/apireference.rst index 9e6ed474..304755fc 100644 --- a/docs/apireference.rst +++ b/docs/apireference.rst @@ -31,6 +31,9 @@ Documents .. autoclass:: mongoengine.document.MapReduceDocument :members: +.. autoclass:: mongoengine.ValidationError + :members: + Querying ======== diff --git a/docs/guide/defining-documents.rst b/docs/guide/defining-documents.rst index 1e404c03..ba7a5801 100644 --- a/docs/guide/defining-documents.rst +++ b/docs/guide/defining-documents.rst @@ -98,7 +98,7 @@ arguments can be set on all fields: :attr:`required` (Default: False) If set to True and the field is not set on the document instance, a - :class:`~mongoengine.base.ValidationError` will be raised when the document is + :class:`~mongoengine.ValidationError` will be raised when the document is validated. :attr:`default` (Default: None) diff --git a/docs/guide/document-instances.rst b/docs/guide/document-instances.rst index 4e799ec7..54fa804b 100644 --- a/docs/guide/document-instances.rst +++ b/docs/guide/document-instances.rst @@ -91,5 +91,5 @@ is an alias to :attr:`id`:: .. note:: If you define your own primary key field, the field implicitly becomes - required, so a :class:`ValidationError` will be thrown if you don't provide - it. + required, so a :class:`~mongoengine.ValidationError` will be thrown if + you don't provide it. diff --git a/mongoengine/base.py b/mongoengine/base.py index adb40d87..6007d3af 100644 --- a/mongoengine/base.py +++ b/mongoengine/base.py @@ -25,7 +25,15 @@ class InvalidDocumentError(Exception): class ValidationError(AssertionError): """Validation exception. + + May represent an error validating a field or a + document containing fields with validation errors. + + :ivar errors: A dictionary of errors for fields within this + document or list, or None if the error is for an + individual field. """ + errors = {} field_name = None _message = None @@ -43,10 +51,12 @@ class ValidationError(AssertionError): def __getattribute__(self, name): message = super(ValidationError, self).__getattribute__(name) - if name == 'message' and self.field_name: - return message + ' ("%s")' % self.field_name - else: - return message + if name == 'message': + if self.field_name: + message += ' ("%s")' % self.field_name + if self.errors: + message += ':\n' + self._format_errors() + return message def _get_message(self): return self._message @@ -57,6 +67,13 @@ class ValidationError(AssertionError): message = property(_get_message, _set_message) def to_dict(self): + """Returns a dictionary of all errors within a document + + Keys are field names or list indices and values are the + validation error messages, or a nested dictionary of + errors for an embedded document or list. + """ + def build_dict(source): errors_dict = {} if not source: @@ -73,6 +90,20 @@ class ValidationError(AssertionError): return {} return build_dict(self.errors) + def _format_errors(self): + """Returns a string listing all errors within a document""" + + def format_error(field, value, prefix=''): + if isinstance(value, dict): + new_prefix = (prefix + '.' if prefix else '') + str(field) + return '\n'.join( + [format_error(k, value[k], new_prefix) for k in value]) + else: + return (prefix + ": " if prefix else '') + str(value) + + return '\n'.join( + [format_error(k, v) for k, v in self.to_dict().items()]) + _document_registry = {}