19

我将 pydanticBaseModel与这样的验证器一起使用:

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_datevalidate_date我的自定义验证器之后运行,如果返回,它将失败None

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

4

3 回答 3

21

如果您希望能够根据另一个字段动态修改一个字段,您可以使用values参数。它包含所有先前的字段,并且小心:顺序很重要。您可以使用 avalidator或 a来执行此操作root_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_list但是正如我所说,如果您交换and的顺序some_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))
于 2020-12-03T15:09:24.190 回答
4

您应该可以values根据pydantic docs使用

您还可以将以下参数的任何子集添加到签名中(名称必须匹配):

values:包含任何先前验证字段的名称到值映射的字典

配置:模型配置

field:正在验证的字段

**kwargs:如果提供,这将包括上述未在签名中明确列出的参数

@validator()
def set_value_to_zero(cls, v, values):
    # look up other value in values, set v accordingly.

于 2020-01-21T21:12:56.327 回答
4

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


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

但是,退后一步,您使用aliasand 标志的方法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]
于 2019-01-03T19:39:45.003 回答