Repository: marshmallow-code/marshmallow-jsonapi

Issue:
Validation errors for nested fields are not formatted properly
```
from marshmallow import validate, Schema as BasicSchema
from marshmallow_jsonapi import Schema, fields


class SecondNestedSchema(BasicSchema):
    second = fields.String(validate=validate.OneOf(['test']))


class FirstNestedSchema(BasicSchema):
    first = fields.String(validate=validate.OneOf(['test']))
    second_nest = fields.Nested(SecondNestedSchema, many=True)


class SimpleSchema(Schema):
    id = fields.UUID(dump_only=True)
    simple = fields.String(validate=validate.OneOf(['test']))
    first_nest = fields.Nested(FirstNestedSchema, many=True)

    class Meta:
        type_ = 'simples'

###########################################################

schema = SimpleSchema()
data = {
    'data': {
        'type': 'simples',
        'attributes': {
            'simple': 'tes',
            'first_nest': [
                {
                    'first': 'tes',
                    'second_nest': [
                        {
                            'second': 'tes'
                        }
                    ]
                }
            ]
        }
    }
}
try:
    data = schema.load(data)
    print(data)
except ValidationError as err:
    print(err.messages)

```
This will be printed:
```
{
  'errors': [
    {
      'detail': 0,
      'source': {
        'pointer': '/data/attributes/first_nest'
      }
    },
    {
      'detail': 'Not a valid choice.',
      'source': {
        'pointer': '/data/attributes/simple'
      }
    }
  ]
}
```
This is what I wished for:
```
{
  'errors': [
    {
      'detail': 'Not a valid choice.',
      'source': {
        'pointer': '/data/attributes/first_nest/first'
      }
    },
    {
      'detail': 'Not a valid choice.',
      'source': {
        'pointer': '/data/attributes/first_nest/second_nest/second'
      }
    },
    {
      'detail': 'Not a valid choice.',
      'source': {
        'pointer': '/data/attributes/simple'
      }
    }
  ]
}
```
Is there a possibility for the desired output to happen?

Comments:

Patch:
diff --git a/AUTHORS.rst b/AUTHORS.rst
index 05a075a..5813f7c 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -31,3 +31,4 @@ Contributors (chronological)
 - `@aberres <https://github.com/aberres>`_
 - George Alton `@georgealton <https://github.com/georgealton>`_
 - Areeb Jamal `@iamareebjamal <https://github.com/iamareebjamal>`_
+- Suren Khorenyan `@mahenzon <https://github.com/mahenzon>`_
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 544309a..50ad354 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,5 +1,5 @@
 marshmallow>=2.0.0rc1
 Flask==1.1.1
-sphinx==2.4.2
+sphinx==2.4.4
 sphinx-rtd-theme==0.4.3
 sphinx-issues>=0.2.0
diff --git a/marshmallow_jsonapi/schema.py b/marshmallow_jsonapi/schema.py
index b8de86a..2224516 100644
--- a/marshmallow_jsonapi/schema.py
+++ b/marshmallow_jsonapi/schema.py
@@ -1,3 +1,5 @@
+import itertools
+
 import marshmallow as ma
 from marshmallow.exceptions import ValidationError
 from marshmallow.utils import is_collection
@@ -286,21 +288,34 @@ class Schema(ma.Schema):
 
         formatted_errors = []
         if many:
-            for index, errors in errors.items():
-                for field_name, field_errors in errors.items():
-                    formatted_errors.extend(
-                        [
-                            self.format_error(field_name, message, index=index)
-                            for message in field_errors
-                        ]
-                    )
+            for index, i_errors in errors.items():
+                formatted_errors.extend(self._get_formatted_errors(i_errors, index))
         else:
-            for field_name, field_errors in errors.items():
-                formatted_errors.extend(
-                    [self.format_error(field_name, message) for message in field_errors]
-                )
+            formatted_errors.extend(self._get_formatted_errors(errors))
+
         return {"errors": formatted_errors}
 
+    def _get_formatted_errors(self, errors, index=None):
+        return itertools.chain(
+            *(
+                [
+                    self.format_error(field_name, message, index=index)
+                    for message in field_errors
+                ]
+                for field_name, field_errors in itertools.chain(
+                    *(self._process_nested_errors(k, v) for k, v in errors.items())
+                )
+            )
+        )
+
+    def _process_nested_errors(self, name, data):
+        if not isinstance(data, dict):
+            return [(name, data)]
+
+        return itertools.chain(
+            *(self._process_nested_errors(f"{name}/{k}", v) for k, v in data.items())
+        )
+
     def format_error(self, field_name, message, index=None):
         """Override-able hook to format a single error message as an Error object.
 


Test patch:
diff --git a/tests/test_schema.py b/tests/test_schema.py
index 4ca16ea..bd6ad4b 100755
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -1,4 +1,5 @@
 import pytest
+import marshmallow as ma
 from marshmallow import ValidationError
 
 from marshmallow_jsonapi import Schema, fields
@@ -608,6 +609,56 @@ class TestErrorFormatting:
         assert id_err["source"]["pointer"] == "/data/1/id"
         assert id_err["detail"] == "Not a valid string."
 
+    def test_nested_fields_error(self):
+        min_size = 10
+
+        class ThirdLevel(ma.Schema):
+            number = fields.Int(required=True, validate=ma.validate.Range(min=min_size))
+
+        class SecondLevel(ma.Schema):
+            foo = fields.Str(required=True)
+            third = fields.Nested(ThirdLevel)
+
+        class FirstLevel(Schema):
+            class Meta:
+                type_ = "first"
+
+            id = fields.Int()
+            second = fields.Nested(SecondLevel)
+
+        schema = FirstLevel()
+        result = schema.validate(
+            {
+                "data": {
+                    "type": "first",
+                    "attributes": {"second": {"third": {"number": 5}}},
+                }
+            }
+        )
+
+        def sort_func(d):
+            return d["source"]["pointer"]
+
+        expected_errors = sorted(
+            [
+                {
+                    "source": {"pointer": "/data/attributes/second/third/number"},
+                    "detail": f"Must be greater than or equal to {min_size}."
+                    if _MARSHMALLOW_VERSION_INFO[0] >= 3
+                    else f"Must be at least {min_size}.",
+                },
+                {
+                    "source": {"pointer": "/data/attributes/second/foo"},
+                    "detail": ma.fields.Field.default_error_messages["required"],
+                },
+            ],
+            key=sort_func,
+        )
+
+        errors = sorted(result["errors"], key=sort_func)
+
+        assert errors == expected_errors
+
 
 class TestMeta:
     shape = {