Pydantic:根据其他字段的值在验证器中创建字段 None
Pydantic: Make field None in validator based on other field's value
我正在使用 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_list
的值计算 some_date
的值,并在满足特定条件时使它成为 None
。
我的 JSON 从不包含字段 some_date
,它总是根据 some_list
填充,因此 pre=True, always=True
。但是 some_date
的默认验证器将 运行 在 我的自定义验证器之后,如果 validate_date
returns None
将失败.
有没有办法创建这样一个仅由另一个计算的字段,并且仍然可以是 Optional
?
更新:正如其他人所指出的,现在可以使用较新的版本(>=0.20)来完成。参见 。 (旁注:即使 OP 的代码现在也可以工作,但不使用别名就更好了。)
从略读文档和 pydantic 的来源,我倾向于说 pydantic 的验证机制目前对类型转换的支持非常有限(list -> date
,list -> 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]
你应该可以使用values
you can also add any subset of the following arguments to the
signature (the names must match):
values: a dict containing the
name-to-value mapping of any previously-validated fields
config: the
model config
field: the field being validated
**kwargs: if provided, this will include the arguments above not explicitly listed in the signature
@validator()
def set_value_to_zero(cls, v, values):
# look up other value in values, set v accordingly.
如果你想根据另一个字段动态修改一个字段,你可以使用values
参数。它包含所有先前的字段,并且注意:顺序很重要。您可以使用 validator
或 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
和 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))
我正在使用 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_list
的值计算 some_date
的值,并在满足特定条件时使它成为 None
。
我的 JSON 从不包含字段 some_date
,它总是根据 some_list
填充,因此 pre=True, always=True
。但是 some_date
的默认验证器将 运行 在 我的自定义验证器之后,如果 validate_date
returns None
将失败.
有没有办法创建这样一个仅由另一个计算的字段,并且仍然可以是 Optional
?
更新:正如其他人所指出的,现在可以使用较新的版本(>=0.20)来完成。参见
从略读文档和 pydantic 的来源,我倾向于说 pydantic 的验证机制目前对类型转换的支持非常有限(list -> date
,list -> 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]
values
you can also add any subset of the following arguments to the signature (the names must match):
values: a dict containing the name-to-value mapping of any previously-validated fields
config: the model config
field: the field being validated
**kwargs: if provided, this will include the arguments above not explicitly listed in the signature
@validator()
def set_value_to_zero(cls, v, values):
# look up other value in values, set v accordingly.
如果你想根据另一个字段动态修改一个字段,你可以使用values
参数。它包含所有先前的字段,并且注意:顺序很重要。您可以使用 validator
或 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
和 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))