【问题标题】:Pydantic: Transform a value before it is assigned to a field?Pydantic:在将值分配给字段之前对其进行转换?
【发布时间】:2022-07-11 01:18:22
【问题描述】:

我有以下型号

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 支持这个吗?

【问题讨论】:

    标签: python python-3.x pydantic


    【解决方案1】:

    您可以使用 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)
    

    【讨论】:

    • 谢谢! pre=True 关键字是缺失的部分......我想补充一点,如果不需要 allow_reuse 功能,validator 可以用作装饰器。
    【解决方案2】:

    只是为了分享一个基于 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)'}}}
    

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

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

    【讨论】:

      猜你喜欢
      • 2018-05-12
      • 2019-11-20
      • 1970-01-01
      • 1970-01-01
      • 2020-07-30
      • 2013-04-20
      • 2021-10-01
      • 1970-01-01
      • 2021-10-26
      相关资源
      最近更新 更多