Repository: marshmallow-code/marshmallow

Issue:
Field-level validation errors cannot be saved on a nested field with many=True
When validating a field, all of its error messages are saved and appended to the list of messages for that field name in `call_and_store`, but for `fields.Nested` with `many=True` the error messages for child elements of the collection are saved and stored in a dictionary keyed by the element index. This causes the following exception on this minimal test case:
```python
class Inner(Schema):
    req = fields.Field(required=True)

class Outer(Schema):
    inner = fields.Nested(Inner, many=True)

    @validates('inner')
    def validates_inner(self, data):
        raise ValidationError('not a chance')

outer = Outer()
_, errors = outer.load({'inner': [{}]})
```
```pytb
Traceback (most recent call last):
  File "/home/andrew/misc/marshmallow/tests/test_schema.py", line 1238, in test_all_errors_on_many_nested_field_with_validates_decorator
    _, errors = outer.load({'inner': [{}]})
  File "/home/andrew/misc/marshmallow/marshmallow/schema.py", line 539, in load
    result, errors = self._do_load(data, many, partial=partial, postprocess=True)
  File "/home/andrew/misc/marshmallow/marshmallow/schema.py", line 610, in _do_load
    self._invoke_field_validators(data=result, many=many)
  File "/home/andrew/misc/marshmallow/marshmallow/schema.py", line 789, in _invoke_field_validators
    field_obj=field_obj
  File "/home/andrew/misc/marshmallow/marshmallow/marshalling.py", line 74, in call_and_store
    errors.setdefault(field_name, []).extend(err.messages)
AttributeError: 'dict' object has no attribute 'extend'
```

Discussion:
nelfin: Not really sure what's happened, all the Travis builds on Python 3.x failed with:
```pytb
Traceback (most recent call last):
  File "setup.py", line 81, in <module>
    cmdclass={'test': PyTest},
  File "/opt/python/3.3.5/lib/python3.3/distutils/core.py", line 148, in setup
    dist.run_commands()
  File "/opt/python/3.3.5/lib/python3.3/distutils/dist.py", line 930, in run_commands
    self.run_command(cmd)
  File "/opt/python/3.3.5/lib/python3.3/distutils/dist.py", line 948, in run_command
    cmd_obj.ensure_finalized()
  File "/opt/python/3.3.5/lib/python3.3/distutils/cmd.py", line 107, in ensure_finalized
    self.finalize_options()
  File "setup.py", line 15, in finalize_options
    self.test_args = ['--verbose', 'tests/']
AttributeError: can't set attribute
```
but `tox -e py34` works fine for me. :confused:
nelfin: Looks like this is being bitten by https://bitbucket.org/pypa/setuptools/commits/cf565b66b855dd4df189b679206f9fb113681737:
```diff
diff --git a/setuptools/command/test.py b/setuptools/command/test.py
--- a/setuptools/command/test.py
+++ b/setuptools/command/test.py
@@ -72,10 +72,6 @@
                 "You may specify a module or a suite, but not both"
             )
 
-        self.test_args = [self.test_suite]
-
-        if self.verbose:
-            self.test_args.insert(0, '--verbose')
         if self.test_loader is None:
             self.test_loader = getattr(self.distribution, 'test_loader', None)
         if self.test_loader is None:
@@ -83,6 +79,11 @@
         if self.test_runner is None:
             self.test_runner = getattr(self.distribution, 'test_runner', None)
 
+    @property
+    def test_args(self):
+        verbose = ['--verbose'] if self.verbose else []
+        return verbose + [self.test_suite]
+
     def with_project_on_sys_path(self, func):
         with_2to3 = PY3 and getattr(self.distribution, 'use_2to3', False)
```
nelfin: Depends on #301 
sloria: Thanks for this patch. I will looks into this more closely tomorrow.

Updating to the latest 2.1-line branch should fix the builds.

Patch:
diff --git a/marshmallow/marshalling.py b/marshmallow/marshalling.py
index 5caca583..7961f087 100644
--- a/marshmallow/marshalling.py
+++ b/marshmallow/marshalling.py
@@ -21,6 +21,8 @@ __all__ = [
     'Unmarshaller',
 ]
 
+# Key used for field-level validation errors on nested fields
+FIELD = '_field'
 
 class ErrorStore(object):
 
@@ -68,6 +70,8 @@ class ErrorStore(object):
             # Warning: Mutation!
             if isinstance(err.messages, dict):
                 errors[field_name] = err.messages
+            elif isinstance(errors.get(field_name), dict):
+                errors[field_name].setdefault(FIELD, []).extend(err.messages)
             else:
                 errors.setdefault(field_name, []).extend(err.messages)
             # When a Nested field fails validation, the marshalled data is stored


Test patch:
diff --git a/tests/test_schema.py b/tests/test_schema.py
index 81d5d0fb..a18c7787 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -1222,6 +1222,23 @@ class TestNestedSchema:
         _, errors = schema.load({'inner': 1})
         assert errors['inner']['_schema'] == ['Invalid input type.']
 
+    # regression test for https://github.com/marshmallow-code/marshmallow/issues/298
+    def test_all_errors_on_many_nested_field_with_validates_decorator(self):
+        class Inner(Schema):
+            req = fields.Field(required=True)
+
+        class Outer(Schema):
+            inner = fields.Nested(Inner, many=True)
+
+            @validates('inner')
+            def validates_inner(self, data):
+                raise ValidationError('not a chance')
+
+        outer = Outer()
+        _, errors = outer.load({'inner': [{}]})
+        assert 'inner' in errors
+        assert '_field' in errors['inner']
+
     def test_missing_required_nested_field(self):
         class Inner(Schema):
             inner_req = fields.Field(required=True, error_messages={'required': 'Oops'})