Skip to content

Commit 38630f6

Browse files
committed
__pydantic_extra__ alwasy a dict if extra=allow
1 parent 3e1fb94 commit 38630f6

File tree

4 files changed

+20
-17
lines changed

4 files changed

+20
-17
lines changed

src/serializers/fields.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,8 @@ impl GeneralFieldsSerializer {
115115
fn extract_dicts<'a>(&self, value: &'a PyAny) -> Option<(&'a PyDict, Option<&'a PyDict>)> {
116116
match self.mode {
117117
FieldsMode::ModelExtra => {
118-
if let Ok((main_dict, extra_dict)) = value.extract::<(&PyDict, &PyAny)>() {
119-
if let Ok(extra_dict) = extra_dict.downcast::<PyDict>() {
120-
Some((main_dict, Some(extra_dict)))
121-
} else {
122-
Some((main_dict, None))
123-
}
118+
if let Ok((main_dict, extra_dict)) = value.extract::<(&PyDict, &PyDict)>() {
119+
Some((main_dict, Some(extra_dict)))
124120
} else {
125121
None
126122
}

src/validators/model_fields.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,13 @@ impl Validator for ModelFieldsValidator {
249249
Err(ValError::LineErrors(errors))
250250
} else {
251251
let fields_set = PySet::new(py, &fields_set_vec)?;
252+
253+
// if we have extra=allow, but we didn't create a dict because we were validate attributes, set it now
254+
// so __pydantic_extra__ is always a dict if extra=allow
255+
if model_extra_dict_op.is_none() && matches!(self.extra_behavior, ExtraBehavior::Allow) {
256+
model_extra_dict_op = Some(PyDict::new(py));
257+
};
258+
252259
Ok((model_dict, model_extra_dict_op, fields_set).to_object(py))
253260
}
254261
}

tests/serializers/test_model.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ def test_model_allow_extra():
121121
extra_behavior='allow',
122122
)
123123
)
124-
assert s.to_python(BasicModel(foo=1, bar=b'more', __pydantic_extra__=None)) == IsStrictDict(foo=1, bar=b'more')
125-
assert s.to_python(BasicModel(bar=b'more', foo=1, __pydantic_extra__=None)) == IsStrictDict(bar=b'more', foo=1)
124+
assert s.to_python(BasicModel(foo=1, bar=b'more', __pydantic_extra__={})) == IsStrictDict(foo=1, bar=b'more')
125+
assert s.to_python(BasicModel(bar=b'more', foo=1, __pydantic_extra__={})) == IsStrictDict(bar=b'more', foo=1)
126126
assert s.to_python(BasicModel(foo=1, __pydantic_extra__=dict(c=3), bar=b'more')) == IsStrictDict(
127127
foo=1, bar=b'more', c=3
128128
)

tests/validators/test_model_fields.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,11 +1107,11 @@ class MyDataclass:
11071107
}
11081108
)
11091109

1110-
assert v.validate_python(Foobar()) == ({'a': 1}, None, {'a'})
1111-
assert v.validate_python(MyDataclass()) == ({'a': 1}, None, {'a'})
1112-
assert v.validate_python(Cls(a=1, b=2, c='ham')) == ({'a': 1}, None, {'a'})
1113-
assert v.validate_python(Cls(a=1, b=datetime(2000, 1, 1))) == ({'a': 1}, None, {'a'})
1114-
assert v.validate_python(Cls(a=1, b=datetime.now, c=lambda: 42)) == ({'a': 1}, None, {'a'})
1110+
assert v.validate_python(Foobar()) == ({'a': 1}, {}, {'a'})
1111+
assert v.validate_python(MyDataclass()) == ({'a': 1}, {}, {'a'})
1112+
assert v.validate_python(Cls(a=1, b=2, c='ham')) == ({'a': 1}, {}, {'a'})
1113+
assert v.validate_python(Cls(a=1, b=datetime(2000, 1, 1))) == ({'a': 1}, {}, {'a'})
1114+
assert v.validate_python(Cls(a=1, b=datetime.now, c=lambda: 42)) == ({'a': 1}, {}, {'a'})
11151115

11161116

11171117
def test_from_attributes_extra_ignore_no_attributes_accessed() -> None:
@@ -1360,8 +1360,8 @@ def test_alias_extra_from_attributes():
13601360
}
13611361
)
13621362
assert v.validate_python({'FieldA': 1}) == ({'field_a': 1}, {}, {'field_a'})
1363-
assert v.validate_python(Cls(FieldA=1)) == ({'field_a': 1}, None, {'field_a'})
1364-
assert v.validate_python(Cls(foo=[1, 2, 3])) == ({'field_a': 3}, None, {'field_a'})
1363+
assert v.validate_python(Cls(FieldA=1)) == ({'field_a': 1}, {}, {'field_a'})
1364+
assert v.validate_python(Cls(foo=[1, 2, 3])) == ({'field_a': 3}, {}, {'field_a'})
13651365
assert v.validate_python({'foo': [1, 2, 3]}) == ({'field_a': 3}, {}, {'field_a'})
13661366

13671367

@@ -1377,8 +1377,8 @@ def test_alias_extra_by_name(py_and_json: PyAndJson):
13771377
)
13781378
assert v.validate_test({'FieldA': 1}) == ({'field_a': 1}, {}, {'field_a'})
13791379
assert v.validate_test({'field_a': 1}) == ({'field_a': 1}, {}, {'field_a'})
1380-
assert v.validate_python(Cls(FieldA=1)) == ({'field_a': 1}, None, {'field_a'})
1381-
assert v.validate_python(Cls(field_a=1)) == ({'field_a': 1}, None, {'field_a'})
1380+
assert v.validate_python(Cls(FieldA=1)) == ({'field_a': 1}, {}, {'field_a'})
1381+
assert v.validate_python(Cls(field_a=1)) == ({'field_a': 1}, {}, {'field_a'})
13821382

13831383

13841384
def test_alias_extra_forbid(py_and_json: PyAndJson):

0 commit comments

Comments
 (0)