Messages
Basic
Ensurance Messages
Passby a string.
>>> z.ensure(lambda x: len(x) > 5 , message='Too short').parse("hello")
Traceback (most recent call last):
zangar.exceptions.ValidationError: [{'msgs': ['Too short']}]
Passby a function.
>>> z.ensure(lambda x: len(x) > 5 , message=lambda x: f'The ${x} is too short').parse("hello")
Traceback (most recent call last):
zangar.exceptions.ValidationError: [{'msgs': ['The $hello is too short']}]
Transformation Messages
Passby a string.
>>> z.transform(int, message='Invalid integer').parse('a')
Traceback (most recent call last):
zangar.exceptions.ValidationError: [{'msgs': ['Invalid integer']}]
Passby a function.
>>> z.transform(int, message=lambda x: f'Invalid integer: {x!r}').parse('a')
Traceback (most recent call last):
zangar.exceptions.ValidationError: [{'msgs': ["Invalid integer: 'a'"]}]
Type messages
API like z.int
and z.to.int
are implemented based on ensure
and transform
, and they also support the above-mentioned custom message mechanism.
z.str(message='Invalid string')
z.int(message='Invalid integer')
z.float(message='Invalid float')
z.to.str(message='Invalid string')
z.to.int(message='Invalid integer')
...
Required field message
>>> z.object({
... 'username': z.field(z.str()).required(message='Username is required.'),
... }).parse({})
Traceback (most recent call last):
zangar.exceptions.ValidationError: [{'loc': ['username'], 'msgs': ['Username is required.']}]
Warning
If the required field message is a function, the function's argument will be a None
.
Different messages
Message can be in any form you need, not just a string.
Note
In practical scenarios, the frontend often needs the backend to provide an error code rather than an exact error message. This is because the frontend may have its own multilingual settings or custom text requirements.
import enum
import json
class ErrorCode(enum.Enum):
INVALID = 1000, 'Invalid value.'
REQUIRED = 1001, 'This field is required.'
IS_EMAIL = 1002, 'Must provide a valid email address.'
def custom_default(o):
if isinstance(o, enum.Enum):
return {'code': o.value[0], 'description': o.value[1]}
raise TypeError(f'Cannot serialize object of {type(obj)}')
schema = z.object({
'username': z.field(z.str()).required(message=ErrorCode.REQUIRED)
})
>>> try:
... schema.parse({})
... except z.ValidationError as e:
... print(json.dumps(e.format_errors(), default=custom_default, indent=2))
[
{
"loc": [
"username"
],
"msgs": [
{
"code": 1001,
"description": "This field is required."
}
]
}
]
Modify default messages
The default messages in Zangar's validation methods are all string types. Overriding these default messages one by one can be costly. Therefore, Zangar offers a context object that allows modifying default messages wherever needed.
import zangar as z
class MyDefaultMessages(z.DefaultMessages):
def default(self, name: str, value, ctx: dict):
if name == 'field_required':
return {
'error': 1001,
'reason': 'This field is required.'
}
return {
'error': 1000,
'reason': super().default(name, value, ctx),
}
>>> with MyDefaultMessages():
... try:
... z.object({
... 'username': z.str().min(6),
... 'password': z.str()
... }).parse({'username': 'user'})
... except z.ValidationError as e:
... print(json.dumps(e.format_errors(), indent=2))
[
{
"loc": [
"username"
],
"msgs": [
{
"error": 1000,
"reason": "The minimum length of the string is 6"
}
]
},
{
"loc": [
"password"
],
"msgs": [
{
"error": 1001,
"reason": "This field is required."
}
]
}
]
This is the source code for Zangar DefaultMessages.default
.
def default(self, name: str, value: Any, ctx: dict):
if name == "field_required":
return "This field is required"
if name == "type_check":
return f"Expected {ctx['expected_type'].__name__}, received {type(value).__name__}"
if name == "type_convertion":
return (
f"Cannot convert the value {value!r} to {ctx['expected_type'].__name__}"
)
if name == "transform_failed":
return str(ctx["exc"])
if name == "str_min":
return f"The minimum length of the string is {ctx['min']}"
if name == "str_max":
return f"The maximum length of the string is {ctx['max']}"
if name == "number_gte":
return f"The value should be greater than or equal to {ctx['gte']}"
if name == "number_gt":
return f"The value should be greater than {ctx['gt']}"
if name == "number_lte":
return f"The value should be less than or equal to {ctx['lte']}"
if name == "number_lt":
return f"The value should be less than {ctx['lt']}"
if name == "datetime_is_aware":
return "The datetime should be aware"
if name == "datetime_is_naive":
return "The datetime should be naive"
return "Invalid value"