Skip to content

Inconsistent value type when validating nested schema fields #2961

Description

@torotil

Problem:

When a Nested schema has a validation error its @post_load hooks are not invoked bypassing any transformations done there. This means the parent schema’s @validates and @validate_schema hooks have to deal with both the transformed and untransformed child values.

Originally I encountered this using marshmallow-sqlalchemy with load_instance=True.

Steps to reproduce:

The code below shows the problem.

import dataclasses
import uuid

import marshmallow as ma
import pytest


@dataclasses.dataclass
class ChildModel:
    uuid: uuid.UUID
    name: str


class Child(ma.Schema):
    uuid = ma.fields.UUID(required=True)
    name = ma.fields.String(required=True)

    @ma.post_load
    def transform(self, data, **_kwargs):
        return ChildModel(**data)


class Parent(ma.Schema):
    child = ma.fields.Nested("Child")

    @ma.validates("child")
    def validate_child(self, value, **_kwargs):
        if value:
            # This fails when the child has a validation error as then its @post_load is not called.
            assert isinstance(value, ChildModel)


def test_load_valid_uuid():
    data = {"child": {"uuid": "c81505b4-258b-4912-b8bc-8ac913d56736", "name": "test"}}
    result = Parent().load(data)
    assert isinstance(result["child"], ChildModel)


def test_load_invalid_uuid():
    data = {"child": {"uuid": "invalid-uuid", "name": "test"}}
    with pytest.raises(ma.exceptions.ValidationError):
        result = Parent().load(data)

When the child schema validates then Parent.validate_child is called with a ChildModel instance, but if the child data is invalid it gets called with a dict instance instead.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions