【问题标题】:How do I pass kwargs to pydantic validator如何将 kwargs 传递给 pydantic 验证器
【发布时间】:2020-07-12 16:59:48
【问题描述】:

我正在尝试编写 pydantic validators,但我似乎无法理解如何使用文档中提到的 kwargs 参数。我想传递条件参数进行验证。这是一个玩具示例:

from pydantic import validator
import pydantic.dataclasses as pyd_dc


@pyd_dc.dataclass
class Point_t:
    x: int = 0
    y: int = 1

    @validator("y")
    def quadrant(cls, val, values, **kwargs):
        pt = x, y = values.get("x", None), val
        if x is None:
            raise ValueError(f"invalid point: {x}, {y}")

        signs = kwargs.get("signs", None)
        if signs is None:
            raise ValueError("'signs' parameter missing")

        if all(c * s >= 0 for c, s in zip(pt, signs) if s != 0):
            return val

        raise ValueError(f"{pt} not in quadrant: {signs}")

这似乎不起作用,实例化Point_t 对象会导致验证错误:

ValidationError: 1 validation error for Point_t
y
  'signs' parameter missing (type=value_error)

如何传递上例中的signs 参数?如果没有办法,允许**kwargs 有什么意义?我错过了什么?

【问题讨论】:

    标签: python-3.x validation python-dataclasses pydantic


    【解决方案1】:

    因为Pydantic.validators 在这里是类方法

    允许 **kwargs 有什么意义?

    由于验证器是“类方法”,这里的完整签名等于 (cls, value, *, values, config, field)

    换句话说,您的 def quadrant(..., **kwargs):euqalconfig, field

    如何将 kwargs 传递给 pydantic 验证器

    1) 创建自定义config 并从配置中读取

    2) 字段破解

    class Point_t:
        signs = dict
        x: int = 0
    
        def quadrant(cls, val, values, **kwargs):
            signs = values[signs]
            if signs is None:
                raise ValueError("'signs' parameter missing")
    
            if all(c * s >= 0 for c, s in zip(pt, signs) if s != 0):
                return val
    
    

    【讨论】:

      【解决方案2】:

      我在 pydantic 模型上使用 Config 解决了这个问题。这在创建可重用的验证器时尤其重要。

      from pydantic import validator
      import pydantic.dataclasses as pyd_dc
      
      
      def name_must_match(cls, v: str) -> str:
          """
          Throw an error if the provided value does not match
          the class name (`cls.Config.name`)
      
          Note: for this validator to work, provided pydantic model (`cls`)
          must have a Config attribute, with a `name` attribute.
          """
          if v != cls.Config.name:
              raise ValueError('value does not equal class name')
          return v
      
      # Now to use the validator
      
      @pyd_dc.dataclass
      class Point:
          """Some pydantic model"""
      
          class Config:
              """Store extra info here"""
              name = 'foo'
      
          # define your fields
          name: str = 'foo'
          x: int = 0
          y: int = 1
          
          # Fields with underscores can be hidden from json encoder
          _name =  validator('name',
                              allow_reuse=True,
                              pre=True,
                              always=True
                              )(name_must_match)
      
      # To test it out
      >> Point(name='foo', x=1, y=2)
      Point(name='foo', x=1, y=2)
      
      >> Point(name='bar', x=3, y=4)
      pydantic.error_wrappers.ValidationError: 1 validation error for Point
      name
        value does not equal class name (type=value_error)
      

      【讨论】:

      • 很抱歉,Config 仍然是静态的。在您的示例中,您不能有两个具有不同允许名称的点。我的目标是建立一种验证模式,然后可以针对特定情况对其进行配置和重用。
      • 明白。将值传递给类的初始化就足够了吗?或者,您是否明确地寻找更像是一种方法的东西? @suvayu
      • 所有这些替代方案对于动态代码生成来说都太复杂了(我的实际用例,如上面的评论中所述)。此外,创建一个新字段将配置与数据混合在一起,这对我来说并不理想。自从我提出这个问题以来,我已经通过使用闭包解决了这个问题。我在生成模型之前创建了验证器函数,这并不理想,但可能是最简单的方法。
      • 你可以看看this;它还没有最终确定。 Pydantic 内部对这种代码生成并不完全友好,所以在某些地方我的实现依赖于一些“魔法”(参见make_validator 函数中的 cmets)。
      • 谢谢,我考虑过,我认为他们更专注于(反)序列化、与 API 接口、数据库模型等功能。理想情况下,我更喜欢更轻量级的东西,并且拥有所有这些高级功能作为插件/模块。我将根据我当前实施的可用性经验重新评估。
      猜你喜欢
      • 2022-11-09
      • 2020-06-30
      • 2016-12-11
      • 1970-01-01
      • 2016-03-13
      • 1970-01-01
      • 2021-05-16
      • 2011-06-03
      • 1970-01-01
      相关资源
      最近更新 更多