如何防止 Pydantic 在 ValidationError 上抛出异常

问题描述 投票:0回答:3

如何防止 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 时,分配可能会失败,但我想记录一条警告,允许程序继续,因为在很多情况下这不是一个阻止程序。

python validation pydantic
3个回答
12
投票

您可以使用 pydanticOptional 来保留 None

示例:

from pydantic.schema import Optional, Dict
from pydantic import BaseModel, NonNegativeInt

class Person(BaseModel):
    name: str
    age: NonNegativeInt
    details: Optional[Dict]

这将允许设置空值。


7
投票

这是一个完整的脚本,带有一个新类 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

2
投票

可以使用@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
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.