diff --git a/docs/quickstart.rst b/docs/quickstart.rst index a26eaf767..054a671cc 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -236,7 +236,8 @@ Required Fields You can make a field required by passing ``required=True``. An error will be stored if the the value is missing from the input to :meth:`Schema.load`. -Alternatively, you can provide a custom error message by passing ``required='My custom message'`` (and the required validation will be enabled as if ``required=True``) +Alternatively, you can provide a custom error message by passing ``required='My custom message'`` (and the required validation will be enabled when ``if required`` succeeds). +Dictionaries or lists are also accepted as the custom error message, in case you want to provide more information with the error. .. code-block:: python :emphasize-lines: 2,6 @@ -244,10 +245,14 @@ Alternatively, you can provide a custom error message by passing ``required='My class UserSchema(Schema): name = fields.String(required=True) age = fields.Integer(required='Age is required.') + city = fields.String(required={'message': 'City required', 'code': 400}) email = fields.Email() data, errors = UserSchema().load({'email': 'foo@bar.com'}) - errors # {'name': ['Missing data for required field.'], 'age': ['Age is required.']} + errors + # {'name': ['Missing data for required field.'], + # 'age': ['Age is required.'], + # 'city': {'message': 'City required', 'code': 400}} Schema.validate +++++++++++++++ diff --git a/marshmallow/fields.py b/marshmallow/fields.py index 7bb859b14..8ea50f28d 100644 --- a/marshmallow/fields.py +++ b/marshmallow/fields.py @@ -324,9 +324,11 @@ class Field(FieldABC): its only parameter and returns a boolean. If it returns `False`, an :exc:`UnmarshallingError` is raised. :param required: Raise an :exc:`UnmarshallingError` if the field value - is not supplied during deserialization. If it is a string, this validation - will be enabled and the provided string will be used as the error message - instead of the built in one. + is not supplied during deserialization. If it is not a bool, this validation + will be enabled when `if required` succeeds, and the provided value will be + used as the message of the ValidationError instead of the built in one. + In this case, message can be anything that the ValidationError constructor + accepts (a string, list or dict) :param metadata: Extra arguments to be stored as metadata. .. versionchanged:: 1.0.0 @@ -459,8 +461,9 @@ def deserialize(self, value): def do_deserialization(): if value is missing: if hasattr(self, 'required') and self.required: - message = (self.required if isinstance(self.required, basestring) else - 'Missing data for required field.') + default_message = 'Missing data for required field.' + message = (default_message if isinstance(self.required, bool) else + self.required) raise ValidationError(message) output = self._deserialize(value) self._validate(output) diff --git a/tests/test_deserialization.py b/tests/test_deserialization.py index 35f2dfc13..17d6b7bfa 100644 --- a/tests/test_deserialization.py +++ b/tests/test_deserialization.py @@ -7,7 +7,7 @@ from marshmallow import fields, utils, Schema from marshmallow.exceptions import UnmarshallingError, ValidationError -from marshmallow.compat import text_type, total_seconds +from marshmallow.compat import text_type, total_seconds, basestring from tests.base import ( assert_almost_equal, @@ -1031,13 +1031,15 @@ class ColorSchema(Schema): assert data == {} -def test_required_message_can_be_changed(): - message = 'My custom required message' - +@pytest.mark.parametrize('message', ['My custom required message', + {'error': 'something', 'code': 400}, + ['first error', 'second error']]) +def test_required_message_can_be_changed(message): class RequireSchema(Schema): age = fields.Integer(required=message) user_data = {"name": "Phil"} data, errs = RequireSchema().load(user_data) - assert message in errs['age'] + expected = [message] if isinstance(message, basestring) else message + assert expected == errs['age'] assert data == {}