Skip to content

Commit 000e823

Browse files
committed
Fix ValidationInfo.field_name missing with model_validate_json()
Backport of: #13084
1 parent d45d8be commit 000e823

3 files changed

Lines changed: 44 additions & 21 deletions

File tree

pydantic-core/src/validators/model_fields.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,8 @@ impl ModelFieldsValidator {
629629
// dict, and try to set defaults for any missing fields
630630

631631
for (field, field_result) in std::iter::zip(&self.fields, field_results) {
632+
let state = &mut state.scoped_set_field_name(Some(field.name.as_py_str().bind(py).clone()));
633+
632634
let field_value = if let Some((field_info, field_json_value)) = field_result {
633635
match field.validator.validate(py, field_json_value, state) {
634636
Ok(value) => {

tests/test_validators.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3145,3 +3145,45 @@ class Base(BaseModel):
31453145
) # Create a Sub instance without triggering validation (e.g., using model_construct)
31463146
# Attempt to create Base with the Sub instance. This line should succeed if the bug is fixed, but currently raises ValidationError.
31473147
Base(sub=sub) # <-- This throws AssertionError because Sub's 'after' validator runs again.
3148+
3149+
3150+
def test_model_validate_by_json_field_validator_with_validation_info() -> None:
3151+
"""https://github.com/pydantic/pydantic/issues/13074"""
3152+
3153+
class Foo(BaseModel):
3154+
field1: int
3155+
field2: int
3156+
3157+
@field_validator('field2')
3158+
@classmethod
3159+
def _validate_field2(cls, v: int, info: ValidationInfo) -> int:
3160+
assert info.field_name in ('field1', 'field2')
3161+
assert info.context == 'context'
3162+
3163+
return v + info.data['field1']
3164+
3165+
f1 = Foo.model_validate({'field1': 1, 'field2': 2}, context='context')
3166+
f2 = Foo.model_validate_json('{"field1": 1, "field2": 2}', context='context')
3167+
3168+
assert f1.field1 == f2.field1 == 1
3169+
assert f1.field2 == f2.field2 == 3
3170+
3171+
3172+
def test_model_validate_json_default_value_validator_with_validation_info() -> None:
3173+
"""https://github.com/pydantic/pydantic/issues/13074"""
3174+
3175+
class Foo(BaseModel, validate_default=True):
3176+
field: int = 1
3177+
3178+
@field_validator('field')
3179+
@classmethod
3180+
def _validate_field(cls, v: int, info: ValidationInfo) -> int:
3181+
assert info.field_name == 'field'
3182+
assert info.context == 'context'
3183+
3184+
return v + 1
3185+
3186+
f1 = Foo.model_validate({'field': 1}, context='context')
3187+
f2 = Foo.model_validate_json('{"field1": 1}', context='context')
3188+
3189+
assert f1.field == f2.field == 2

tests/types/test_model.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
ConfigDict,
88
SerializationInfo,
99
TypeAdapter,
10-
ValidationInfo,
11-
field_validator,
1210
model_serializer,
1311
)
1412

@@ -85,22 +83,3 @@ def serialize(self, info: SerializationInfo) -> str:
8583
else:
8684
assert serializer.to_python(ModelB(a=123, b='test'), **kwargs) == 'ModelA'
8785
assert serializer.to_json(ModelB(a=123, b='test'), **kwargs) == b'"ModelA"'
88-
89-
90-
def test_model_validate_by_json_with_validation_info_data():
91-
"""https://github.com/pydantic/pydantic/issues/13074"""
92-
93-
class Foo(BaseModel):
94-
field1: int
95-
field2: int
96-
97-
@field_validator('field2')
98-
@classmethod
99-
def _validate_field2(cls, v: str, info: ValidationInfo) -> int:
100-
return v + info.data['field1']
101-
102-
f1 = Foo.model_validate({'field1': 1, 'field2': 2})
103-
f2 = Foo.model_validate_json('{"field1": 1, "field2": 2}')
104-
105-
assert f1.field1 == f2.field1 == 1
106-
assert f1.field2 == f2.field2 == 3

0 commit comments

Comments
 (0)