Pydantic:根据其他字段的值在验证器中将字段设为 None

新手上路,请多包涵

我正在使用 pydantic BaseModel 和这样的验证器:

 from datetime import date
from typing import List, Optional
from pydantic import BaseModel, BaseConfig, validator

class Model(BaseModel):
    class Config(BaseConfig):
        allow_population_by_alias = True
        fields = {
            "some_date": {
                "alias": "some_list"
            }
        }
    some_date: Optional[date]
    some_list: List[date]

    @validator("some_date", pre=True, always=True)
    def validate_date(cls, value):
        if len(value) < 2: # here value is some_list
            return None
        return value[0] # return the first value - let's assume it's a date string

# This reproduces the problem
m = Model(some_list=['2019-01-03'])

我想根据 --- 的值计算 some_date 的值 some_list 并使其成为 None 如果满足特定条件。

我的 JSON 从不包含字段 some_date ,它总是基于 some_list 填充,因此 pre=True, always=True 。但是 some_date 的默认验证器将 我的自定义验证器之后运行,如果 validate_date 返回 None ,它将失败。

有没有办法创建这样一个仅由另一个计算的字段,并且仍然可以是 Optional

原文由 Dániel Nagy 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.9k
2 个回答

更新:正如其他人指出的那样,现在可以使用较新的版本(>=0.20)来完成。看到 这个答案。 (旁注:即使 OP 的代码现在也能正常工作,但不使用别名就更好了。)


从略读文档和 pydantic 的来源,我倾向于说 pydantic 的验证机制目前对验证函数中的类型转换( list -> datelist -> NoneType )的支持非常有限。

然而,退后一步,您使用 alias 和标志 allow_population_by_alias 的方法似乎有点过载。 some_date 只需要作为 some_list[0] if len(some_list) >= 2 else None 的快捷方式,但它永远不会独立于 some_list 设置。如果真是这样,为什么不选择以下选项呢?

 class Model(BaseModel):
    some_list: List[date] = ...

    @property
    def some_date(self):
        return None if len(self.some_list) < 2 else self.some_list[0]

原文由 normanius 发布,翻译遵循 CC BY-SA 4.0 许可协议

如果你希望能够根据另一个字段动态修改一个字段,你可以使用 values 参数。它包含所有以前的字段,并且要小心: 顺序很重要。您可以使用 validatorroot_validator 来执行此操作。

随着 validator

 >>> from datetime import date
>>> from typing import List, Optional
>>> from pydantic import BaseModel, validator
>>> class Model(BaseModel):
        some_list: List[date]
        some_date: Optional[date]

        @validator("some_date", always=True)
        def validate_date(cls, value, values):
            if len(values["some_list"]) < 2:
                return None
            return values["some_list"][0]

>>> Model(some_list=['2019-01-03', '2020-01-03', '2021-01-03'])
Model(some_list=[datetime.date(2019, 1, 3), datetime.date(2020, 1, 3), datetime.date(2021, 1, 3)],
      some_date=datetime.date(2019, 1, 3))

但正如我所说,如果你交换 some_listsome_date 的顺序,你将得到一个 KeyError: 'some_list'

随着 root_validator

另一种选择是使用 root_validator 。这些作用于所有领域:

 >>> class Model(BaseModel):
        some_list: List[date]
        some_date: Optional[date]

        @root_validator
        def validate_date(cls, values):
            if not len(values["some_list"]) < 2:
                values["some_date"] = values["some_list"][0]
            return values

>>> Model(some_list=['2019-01-03', '2020-01-03', '2021-01-03'])
Model(some_list=[datetime.date(2019, 1, 3), datetime.date(2020, 1, 3), datetime.date(2021, 1, 3)],
      some_date=datetime.date(2019, 1, 3))

原文由 tupui 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题