mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
@@ -0,0 +1 @@
|
||||
Fix const validations for lists
|
||||
@@ -105,7 +105,7 @@ def flatten_errors(
|
||||
else:
|
||||
yield error.dict(config, loc_prefix=loc)
|
||||
elif isinstance(error, list):
|
||||
yield from flatten_errors(error, config)
|
||||
yield from flatten_errors(error, config, loc=loc)
|
||||
else:
|
||||
raise RuntimeError(f'Unknown error object: {error}')
|
||||
|
||||
|
||||
+9
-7
@@ -106,8 +106,8 @@ class Field:
|
||||
self.sub_fields: Optional[List[Field]] = None
|
||||
self.key_field: Optional[Field] = None
|
||||
self.validators: 'ValidatorsList' = []
|
||||
self.whole_pre_validators: Optional['ValidatorsList'] = None
|
||||
self.whole_post_validators: Optional['ValidatorsList'] = None
|
||||
self.whole_pre_validators: 'ValidatorsList' = []
|
||||
self.whole_post_validators: 'ValidatorsList' = []
|
||||
self.parse_json: bool = False
|
||||
self.shape: int = SHAPE_SINGLETON
|
||||
self.prepare()
|
||||
@@ -253,9 +253,8 @@ class Field:
|
||||
else:
|
||||
raise TypeError(f'Fields of type "{origin}" are not supported.')
|
||||
|
||||
if getattr(self.type_, '__origin__', None):
|
||||
# type_ has been refined eg. as the type of a List and sub_fields needs to be populated
|
||||
self.sub_fields = [self._create_sub_type(self.type_, '_' + self.name)]
|
||||
# type_ has been refined eg. as the type of a List and sub_fields needs to be populated
|
||||
self.sub_fields = [self._create_sub_type(self.type_, '_' + self.name)]
|
||||
|
||||
def _create_sub_type(self, type_: AnyType, name: str, *, for_keys: bool = False) -> 'Field':
|
||||
return self.__class__(
|
||||
@@ -272,13 +271,16 @@ class Field:
|
||||
v_funcs = (
|
||||
*[v.func for v in class_validators_ if not v.whole and v.pre],
|
||||
*(get_validators() if get_validators else list(find_validators(self.type_, self.model_config))),
|
||||
self.schema is not None and self.schema.const and constant_validator,
|
||||
*[v.func for v in class_validators_ if not v.whole and not v.pre],
|
||||
)
|
||||
self.validators = self._prep_vals(v_funcs)
|
||||
|
||||
# Add const validator
|
||||
if self.schema is not None and self.schema.const:
|
||||
self.whole_pre_validators = self._prep_vals([constant_validator])
|
||||
|
||||
if class_validators_:
|
||||
self.whole_pre_validators = self._prep_vals(v.func for v in class_validators_ if v.whole and v.pre)
|
||||
self.whole_pre_validators.extend(self._prep_vals(v.func for v in class_validators_ if v.whole and v.pre))
|
||||
self.whole_post_validators = self._prep_vals(v.func for v in class_validators_ if v.whole and not v.pre)
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -397,6 +397,112 @@ def test_const_with_wrong_value():
|
||||
]
|
||||
|
||||
|
||||
def test_const_list():
|
||||
class SubModel(BaseModel):
|
||||
b: int
|
||||
|
||||
class Model(BaseModel):
|
||||
a: List[SubModel] = Schema([SubModel(b=1), SubModel(b=2), SubModel(b=3)], const=True)
|
||||
b: List[SubModel] = Schema([{'b': 4}, {'b': 5}, {'b': 6}], const=True)
|
||||
|
||||
m = Model()
|
||||
assert m.a == [SubModel(b=1), SubModel(b=2), SubModel(b=3)]
|
||||
assert m.b == [SubModel(b=4), SubModel(b=5), SubModel(b=6)]
|
||||
assert m.schema() == {
|
||||
'definitions': {
|
||||
'SubModel': {
|
||||
'properties': {'b': {'title': 'B', 'type': 'integer'}},
|
||||
'required': ['b'],
|
||||
'title': 'SubModel',
|
||||
'type': 'object',
|
||||
}
|
||||
},
|
||||
'properties': {
|
||||
'a': {
|
||||
'const': [SubModel(b=1), SubModel(b=2), SubModel(b=3)],
|
||||
'items': {'$ref': '#/definitions/SubModel'},
|
||||
'title': 'A',
|
||||
'type': 'array',
|
||||
},
|
||||
'b': {
|
||||
'const': [{'b': 4}, {'b': 5}, {'b': 6}],
|
||||
'items': {'$ref': '#/definitions/SubModel'},
|
||||
'title': 'B',
|
||||
'type': 'array',
|
||||
},
|
||||
},
|
||||
'title': 'Model',
|
||||
'type': 'object',
|
||||
}
|
||||
|
||||
|
||||
def test_const_list_with_wrong_value():
|
||||
class SubModel(BaseModel):
|
||||
b: int
|
||||
|
||||
class Model(BaseModel):
|
||||
a: List[SubModel] = Schema([SubModel(b=1), SubModel(b=2), SubModel(b=3)], const=True)
|
||||
b: List[SubModel] = Schema([{'b': 4}, {'b': 5}, {'b': 6}], const=True)
|
||||
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
Model(a=[{'b': 3}, {'b': 1}, {'b': 2}], b=[{'b': 6}, {'b': 5}])
|
||||
|
||||
assert exc_info.value.errors() == [
|
||||
{
|
||||
'ctx': {
|
||||
'given': [{'b': 3}, {'b': 1}, {'b': 2}],
|
||||
'permitted': [[SubModel(b=1), SubModel(b=2), SubModel(b=3)]],
|
||||
},
|
||||
'loc': ('a',),
|
||||
'msg': 'unexpected value; permitted: [<SubModel b=1>, <SubModel b=2>, <SubModel b=3>]',
|
||||
'type': 'value_error.const',
|
||||
},
|
||||
{
|
||||
'ctx': {'given': [{'b': 6}, {'b': 5}], 'permitted': [[{'b': 4}, {'b': 5}, {'b': 6}]]},
|
||||
'loc': ('b',),
|
||||
'msg': "unexpected value; permitted: [{'b': 4}, {'b': 5}, {'b': 6}]",
|
||||
'type': 'value_error.const',
|
||||
},
|
||||
]
|
||||
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
Model(a=[SubModel(b=3), SubModel(b=1), SubModel(b=2)], b=[SubModel(b=3), SubModel(b=1)])
|
||||
|
||||
assert exc_info.value.errors() == [
|
||||
{
|
||||
'ctx': {
|
||||
'given': [SubModel(b=3), SubModel(b=1), SubModel(b=2)],
|
||||
'permitted': [[SubModel(b=1), SubModel(b=2), SubModel(b=3)]],
|
||||
},
|
||||
'loc': ('a',),
|
||||
'msg': 'unexpected value; permitted: [<SubModel b=1>, <SubModel b=2>, <SubModel b=3>]',
|
||||
'type': 'value_error.const',
|
||||
},
|
||||
{
|
||||
'ctx': {'given': [SubModel(b=3), SubModel(b=1)], 'permitted': [[{'b': 4}, {'b': 5}, {'b': 6}]]},
|
||||
'loc': ('b',),
|
||||
'msg': "unexpected value; permitted: [{'b': 4}, {'b': 5}, {'b': 6}]",
|
||||
'type': 'value_error.const',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def test_const_validation_json_serializable():
|
||||
class SubForm(BaseModel):
|
||||
field: int
|
||||
|
||||
class Form(BaseModel):
|
||||
field1: SubForm = Schema({'field': 2}, const=True)
|
||||
field2: List[SubForm] = Schema([{'field': 2}], const=True)
|
||||
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
# Fails
|
||||
Form(field1={'field': 1}, field2=[{'field': 1}])
|
||||
|
||||
# This should not raise an Json error
|
||||
exc_info.value.json()
|
||||
|
||||
|
||||
class ValidateAssignmentModel(BaseModel):
|
||||
a: int = 2
|
||||
b: constr(min_length=1)
|
||||
|
||||
Reference in New Issue
Block a user