Pydantic 数据类句柄 ValidationError

Pydantic dataclass handle ValidationError

我正在寻找将数据传递到 pydantic 数据类时处理 ValidationError 的解决方案。

from pydantic import BaseModel

class TestB(BaseModel):
    id: str
    price : float
    quantity : int

class TestA(BaseModel):
    orderId: str
    totalAmountPaid: float
    products: List[TestB]

data_correct_type = {"orderId": "12341234", "totalAmountPaid": 395.5,
              "products": [{"id": "abcd0001", "price": '299', "quantity": 1},
                           {"id": "abcd0002", "price": '199', "quantity": 1}]}

data_wrong_type = {"orderId": "12341234", "totalAmountPaid": 395.5,
              "products": [{"id": "abcd0001", "price": '299', "quantity": 1},
                           {"id": "abcd0002", "price": 'abc', "quantity": 1}]}

result_pydantic_1 = TestA(**data_correct_type) #works
result_pydantic_2 = TestA(**data_wrong_type) 

output:
raise 1 validation error for TestA
products -> 1 -> price
  value is not a valid float (type=type_error.float)

有什么方法可以用 None 值之类的东西替换错误类型的字段?谢谢!

代码:

from pydantic import BaseModel, validator
from typing import Union

class P(BaseModel):
    string: str
    number: Union[float, None]

    # pre=True → run before standard validators
    @validator('number', pre=True)
    def set_to_none_if_not_a_numeric(cls, v):
        try:
            return float(v)
        except ValueError:
            print('Not a number!')
            return None

a = P(**{"string": "11", "number": "12aa"})
b = P(**{"string": "11", "number": "12"})

print(a)
print('----')
print(b)

输出:

Not a number!
string='11' number=None
----
string='11' number=12.0

回答

what if I want to do it with all the fields?

我建议首先检查pydantic docs。很详细,有很多很好的例子

关于您的问题的示例:

class DemoModel(BaseModel):
    square_numbers: List[int] = []
    cube_numbers: List[int] = []

    # '*' is the same as 'cube_numbers', 'square_numbers' here:
    @validator('*', pre=True)
    def split_str(cls, v):
        if isinstance(v, str):
            return v.split('|')
        return v

    @validator('cube_numbers', 'square_numbers')
    def check_sum(cls, v):
        if sum(v) > 42:
            raise ValueError('sum of numbers greater than 42')
        return v

所以你可以使用@validator('field1', 'field2')@validator('*')

回答

However what I want to achieve is for all the field in the dataclass, it will try to convert to the desired type as defined in the dataclass

发挥你的想象力:

代码:

from pydantic import BaseModel, validator
from typing import Optional


class P(BaseModel):
    string_or_none: Optional[str]
    float_or_none: Optional[float]
    int_or_none: Optional[int]
    bool_or_none: Optional[bool]

    @validator('*', pre=True)
    def cast_var_types(cls, value, field):

        allowed_type = field.type_
        try:
            print(f'Try cast {value} to', str(allowed_type))
            return allowed_type(value)
        except ValueError:
            return None


good_values = P(**{"string_or_none": 1234321, "float_or_none": "12.111", "int_or_none": "987", "bool_or_none": 1})
bad_values = P(**{"string_or_none": "this will be good anyway", "float_or_none": "abc", "int_or_none": "bac", "bool_or_none": "any value can be casted to bool"})

print()
print('  ====================================')
print()

for k in ["string_or_none", "float_or_none", "int_or_none", "bool_or_none"]:
    print(k, "of good values is:")
    print('type →', type(getattr(good_values, k)))
    print('value →', getattr(good_values, k))
    print('----------------')
    print(k, "of bad values is:")
    print('type →', type(getattr(bad_values, k)))
    print('value →', getattr(bad_values, k))
    print('----------------')

并输出:

Try cast 1234321 to <class 'str'>
Try cast 12.111 to <class 'float'>
Try cast 987 to <class 'int'>
Try cast 1 to <class 'bool'>
Try cast this will be good anyway to <class 'str'>
Try cast abc to <class 'float'>
Try cast bac to <class 'int'>
Try cast any value can be casted to bool to <class 'bool'>

  ====================================

string_or_none of good values is:
type → <class 'str'>
value → 1234321
----------------
string_or_none of bad values is:
type → <class 'str'>
value → this will be good anyway
----------------
float_or_none of good values is:
type → <class 'float'>
value → 12.111
----------------
float_or_none of bad values is:
type → <class 'NoneType'>
value → None
----------------
int_or_none of good values is:
type → <class 'int'>
value → 987
----------------
int_or_none of bad values is:
type → <class 'NoneType'>
value → None
----------------
bool_or_none of good values is:
type → <class 'bool'>
value → True
----------------
bool_or_none of bad values is:
type → <class 'bool'>
value → True
----------------