如何防止 Pydantic 在 ValidationError 上抛出异常?
from pydantic import BaseModel, NonNegativeInt
class Person(BaseModel):
name: str
age: NonNegativeInt
details: None
p1: Person = Person(name='Alice', age=30, details=None)
print(p1)
p2: Person = Person(name='Bob', age=40, details={'height': 1.70, 'weight': 91})
print(p1)
我希望这个异常不终止程序并记录警告
name='Alice' age=30
Traceback (most recent call last):
File "playground/test_pydantic_prevent_exception.py", line 12, in <module>
p2: Person = Person(name='Bob', age=40, details={'height': 1.70, 'weight': 91})
File "pydantic/main.py", line 406, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for Person
details
value is not None (type=type_error.not_none)
解释为什么我需要这个的用例如下:
我正在开发一个未 100% 完成的产品,因此返回的值可能会在一夜之间从返回 None 更改为某个其他对象 {},而这在我这边开发时是未知的。
当我查询产品并获取一个对象而不是 None 时,分配可能会失败,但我想记录一条警告,允许程序继续,因为在很多情况下这不是一个阻止程序。
您可以使用 pydanticOptional 来保留 None
示例:
from pydantic.schema import Optional, Dict
from pydantic import BaseModel, NonNegativeInt
class Person(BaseModel):
name: str
age: NonNegativeInt
details: Optional[Dict]
这将允许设置空值。
这是一个完整的脚本,带有一个新类 BaseModelNoException,它继承 Pydantic 的 BaseModel,包装异常 ValidationError 并抛出警告。
import json
from traceback import TracebackException
from typing import no_type_check, Type, Any
from pydantic import BaseModel, NonNegativeInt, ValidationError, StrBytes, Protocol
class BaseModelNoException(BaseModel):
def __init__(__pydantic_self__, **data: Any) -> None:
try:
super(BaseModelNoException, __pydantic_self__).__init__(**data)
except ValidationError as pve:
print(f'This is a warning. __init__ failed to validate:\n {json.dumps(data, indent=4)}\n')
print(f'This is the original exception:\n{pve.json()}')
@no_type_check
def __setattr__(self, name, value):
try:
return super(BaseModelNoException, self).__setattr__(name, value)
except ValidationError as pve:
print(f'This is a warning. __setattr__ failed to validate:\n {json.dumps({name: value}, indent=4)}')
print(f'This is the original exception:\n{pve.json()}')
return None
@classmethod
def parse_obj(cls: Type['Model'], obj: Any) -> 'Model':
try:
return super(BaseModelNoException, cls).parse_obj(obj)
except ValidationError as pve:
print(f'This is a warning. parse_obj failed to validate:\n {json.dumps(obj, indent=4)}')
print(f'This is the original exception:\n{pve.json()}')
return None
@classmethod
def parse_raw(cls: Type['Model'], b: StrBytes, *, content_type: str = None, encoding: str = 'utf8',
proto: Protocol = None, allow_pickle: bool = False, ) -> 'Model':
try:
return super(BaseModelNoException, cls).parse_raw(b=b, content_type=content_type, encoding=encoding,
proto=proto, allow_pickle=allow_pickle)
except ValidationError as pve:
print(f'This is a warning. parse_raw failed to validate:\n {b}')
print(f'This is the original exception:\n{pve.json()}')
return None
# class Person(BaseModel):
class PersonDetails(BaseModel):
height: NonNegativeInt
weight: NonNegativeInt
class Person(BaseModelNoException):
name: str = ''
age: NonNegativeInt = 0
details: PersonDetails
p1: Person = Person(name='Alice', age=30, details=None)
print(p1)
print('-' * 100)
p2: Person = Person(name='Bob', age=40, details={'height': 1.70, 'weight': 91})
print(p2)
print('-' * 100)
p3: Person = Person.parse_obj({'name': 'Alice', 'age': 31, 'details': {'height': 1.70, 'weight': -77}})
print(p3)
print('-' * 100)
print('The End')
这是上述脚本的输出。
/playground/test_pydantic_prevent_exception.py 这是一个警告。 init 验证失败: { “名字”:“爱丽丝”, “年龄”:30, “详细信息”:空 }
This is the original exception:
[
{
"loc": [
"details"
],
"msg": "none is not an allowed value",
"type": "type_error.none.not_allowed"
}
]
----------------------------------------------------------------------------------------------------
name='Bob' age=40 details=PersonDetails(height=1, weight=91)
----------------------------------------------------------------------------------------------------
This is a warning. __init__ failed to validate:
{
"name": "Alice",
"age": 31,
"details": {
"height": 1.7,
"weight": -77
}
}
This is the original exception:
[
{
"loc": [
"details",
"weight"
],
"msg": "ensure this value is greater than or equal to 0",
"type": "value_error.number.not_ge",
"ctx": {
"limit_value": 0
}
}
]
----------------------------------------------------------------------------------------------------
The End
Process finished with exit code 0
可以使用@validator("age", pre=True)
from pydantic import BaseModel, validator
class Person(BaseModel):
name: str
age: int
details: None
@validator("age", pre=True)
def parse_age(cls, value):
if isinstance(value, str):
print('Here is the error text or logging')
return 1
return value