我试图理解如何直接实现python数据类验证。我使用棉花糖validate
来尝试做到这一点,但不理解如何验证实际上可以在数据类中运行,或者它是否只是作为一个元数据字段,您必须相当笨拙地运行。
我可以做一个__post_init__
(如这里和这里所建议的)直接对每个字段执行验证,但我觉得应该有一个更简单的,验证器不确定的方式来验证所有字段根据他们的validate
元数据,无论是在__init__
或其他。
下面是一个示例脚本:
from dataclasses import dataclass, field
from marshmallow import validate
def null_validate(value):
"""Validation fn for dataclass"""
if value is None:
pass
else:
raise ValidationError("{value} should be a string for this dataclass field!")
@dataclass
class Testing:
plus_minus_one: int = field(
default=None,
metadata=dict(
required=False,
validate=validate.OneOf([1, -1])
)
)
max_one: int = field(
default=None,
metadata=dict(
required=False,
validate=validate.Range(max=1)
)
)
null_field: str = field(
default=None,
metadata=dict(
required=False,
validate=null_validate
)
)
print("this passes")
it = Testing(1, 1, None)
print("this should fail")
it = Testing(10, 10, 10)
我像下面这样运行,但是没有得到任何ValidationError
,所以我知道验证不会在数据类中神奇地发生:
% python testing.py
this passes
this should fail
所以我能做的是添加一个__post_init__
方法,像这样的数据类:
def __post_init__(self):
for data_field in self.__dataclass_fields__:
self.__dataclass_fields__[data_field].metadata["validate"](
self.__dict__[data_field]
)
这样,验证或多或少地在参数的基础上工作:
% python testing.py
this passes
this should fail
Traceback (most recent call last):
File "testing.py", line 47, in <module>
it = Testing(10, 10, 10)
File "<string>", line 6, in __init__
File "testing.py", line 41, in __post_init__
self.__dataclass_fields__[data_field].metadata["validate"](self.__dict__[data_field])
File "/Users/max.press/miniconda3/envs/test_env/lib/python3.7/site-packages/marshmallow/validate.py", line 569, in __call__
raise ValidationError(self._format_error(value))
marshmallow.exceptions.ValidationError: Must be one of: 1, -1.
但是这看起来相当笨拙,而且似乎很难实现比这更复杂的验证。似乎我应该能够验证"预先"。当参数传入时,不更改任何内容。
是解决方案移动到一个完整的marshmallow-dataclass
?可能作为Schema
处理这个问题。
事实证明,通过使用棉花糖数据类及其Schema()
方法可以很容易地做到这一点。
下面的代码显示了没有__post_init__
的期望行为,尽管我显然需要阅读更多关于marshmallow的内容:
from dataclasses import dataclass, field
from marshmallow import validate, Schema
from marshmallow_dataclass import dataclass
def null_validate(value):
"""Validation fn for dataclass"""
if value is None:
pass
else:
raise ValidationError("{value} should be a string for this dataclass field!")
@dataclass
class Testing:
plus_minus_one: int = field(
default=None,
metadata=dict(
required=False,
validate=validate.OneOf([1, -1])
)
)
max_one: int = field(
default=None,
metadata=dict(
required=False,
validate=validate.Range(max=1)
)
)
null_field: NoneType = field(
default=None,
metadata=dict(
required=False,
validate=null_validate
)
)
print("this passes")
it = Testing.Schema().load({"plus_minus_one": 1, "max_one": 1, "null_field": None})
print("this should fail")
it = Testing.Schema().load({"plus_minus_one": 10, "max_one": 10, "null_field": 10})
当它运行时,我得到了期望的结果:
this passes
this should fail
[...]
marshmallow.exceptions.ValidationError: {'null_field': ['Not a valid string.'], 'plus_minus_one': ['Must be one of: 1, -1.'], 'max_one': ['Must be less than or equal to 1.']}