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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions