繁体   English   中英

Pydantic:在将值分配给字段之前对其进行转换?

[英]Pydantic: Transform a value before it is assigned to a field?

我有以下型号

class Window(BaseModel):
    size: tuple[int, int]

我想像这样实例化它:

fields = {'size': '1920x1080'}
window = Window(**fields)

当然这会失败,因为'size'的值不是正确的类型。 但是,我想添加逻辑,以便在x处拆分值,即:

def transform(raw: str) -> tuple[int, int]:
    x, y = raw.split('x')
    return int(x), int(y)

Pydantic 支持这个吗?

您可以使用 pydantic 的validator来实现这样的行为。 给定您的预定义功能:

def transform(raw: str) -> tuple[int, int]:
    x, y = raw.split('x')
    return int(x), int(y)

你可以像这样在你的类中实现它:

from pydantic import BaseModel, validator


class Window(BaseModel):
    
    size: tuple[int, int]
    _extract_size = validator('size', pre=True, allow_reuse=True)(transform)

注意传递给验证器的pre=True参数。 这意味着它将在检查size是否为元组的默认验证器之前运行。

现在:

fields = {'size': '1920x1080'}
window = Window(**fields)
print(window)
# output: size=(1920, 1080)

请注意,在那之后,您将无法使用大小的元组来实例化您的Window

fields2 = {'size': (800, 600)}
window2 = Window(**fields2)
# AttributeError: 'tuple' object has no attribute 'split'

为了克服这个问题,如果通过稍微更改代码来传递元组,您可以简单地绕过该函数:

def transform(raw: str) -> tuple[int, int]:
    if type(raw) == tuple:
        return raw
    x, y = raw.split('x')
    return int(x), int(y)

哪个应该给出:

fields2 = {'size': (800, 600)}
window2 = Window(**fields2)
print(window2)
# output: size:(800, 600)

只是为了分享一个基于 convtools 的替代方案:

from convtools.contrib.models import (
    DictModel,
    build,
    casters,
    validate,
    validators,
)

class Window(DictModel):
    size: tuple[int, int] = (
        validate(validators.Type(str))
        .cast(casters.CustomUnsafe(lambda s: s.split("x")))
        .cast()
    )

obj, errors = build(Window, {"size": "1920x1080"})
# In [12]: obj
# Out[12]: Window(size=(1920, 1080))

obj, errors = build(Window, {"size": "1920 1080"})
# In [14]: errors
# Out[14]: {'size': {'__ERRORS': {'length': 'not enough values to unpack (expected 2, got 1)'}}}

或者,如果需要自定义方法:

class Window(DictModel):
    size: tuple[int, int] = field(cls_method=True)

    @classmethod
    def get_size(cls, data):
        try:
            x, y = data["size"].split("x")
            return (int(x), int(y)), None
        except Exception:  # this is bad, but this is only an example
            return None, {"invalid_size": True}

文档: https ://convtools.readthedocs.io/en/latest/models.html

Github: https ://github.com/westandskif/convtools

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM