Repository: marshmallow-code/marshmallow

Issue:
`only` argument inconsistent between Nested(S, many=True) and List(Nested(S))
```python
from pprint import pprint

from marshmallow import Schema
from marshmallow.fields import Integer, List, Nested, String


class Child(Schema):
    name = String()
    age = Integer()


class Family(Schema):
    children = List(Nested(Child))


class Family2(Schema):
    children = Nested(Child, many=True)

family = {'children':[
    {'name': 'Tommy', 'age': 12},
    {'name': 'Lily', 'age': 15},
]}

pprint(Family( only=['children.name']).dump(family).data)
pprint(Family2( only=['children.name']).dump(family).data)
```
returns
```
{'children': [{'age': 12, 'name': 'Tommy'}, {'age': 15, 'name': 'Lily'}]}
{'children': [{'name': 'Tommy'}, {'name': 'Lily'}]}
```

tested with marshmallow 2.15.4

The same applies to `exclude` argument.


Discussion:
For now I'm using following workaround:
```python
class ListFix(List):
    @property
    def only(self):
        return getattr(self.container, 'only')

    @only.setter
    def only(self, new_options):
        original_options = getattr(self.container, 'only', ())
        if original_options:
            new_options &= type(new_options)(original_options)
        setattr(self.container, 'only', new_options)


class Child(Schema):
    name = String()
    age = Integer()


class Family(Schema):
    children = ListFix(Nested(Child))
```

the option propagation code was taken from `BaseSchema.__apply_nested_option`

maybe apply option code (the part I have copied to ListFix property) should be moved to field?

**Edited:** Just found a nasty side effect of my "fix"

```python
family = {'children': [
    {'name': 'Tommy', 'age': 12},
    {'name': 'Lily', 'age': 15},
]}

for family_schema in (
        Family(),
        Family(only=['children.name']),
        Family2(),
        Family2(only=['children.name']),
):
    pprint(family_schema.dump(family).data)
```
prints
```
{'children': [{'name': 'Tommy'}, {'name': 'Lily'}]}
{'children': [{'name': 'Tommy'}, {'name': 'Lily'}]}
{'children': [{'age': 12, 'name': 'Tommy'}, {'age': 15, 'name': 'Lily'}]}
{'children': [{'name': 'Tommy'}, {'name': 'Lily'}]}
```
Thanks @rooterkyberian . Let's continue discussion of this in #779.

Patch:
diff --git a/src/marshmallow/fields.py b/src/marshmallow/fields.py
--- a/src/marshmallow/fields.py
+++ b/src/marshmallow/fields.py
@@ -576,12 +576,18 @@ def __init__(self, cls_or_instance, **kwargs):
                 'The list elements must be a subclass or instance of '
                 'marshmallow.base.FieldABC.',
             )
+        if isinstance(self.container, Nested):
+            self.only = self.container.only
+            self.exclude = self.container.exclude
 
     def _bind_to_schema(self, field_name, schema):
         super()._bind_to_schema(field_name, schema)
         self.container = copy.deepcopy(self.container)
         self.container.parent = self
         self.container.name = field_name
+        if isinstance(self.container, Nested):
+            self.container.only = self.only
+            self.container.exclude = self.exclude
 
     def _serialize(self, value, attr, obj, **kwargs):
         if value is None:
@@ -656,10 +662,11 @@ def _bind_to_schema(self, field_name, schema):
         super()._bind_to_schema(field_name, schema)
         new_tuple_fields = []
         for container in self.tuple_fields:
-            new_container = copy.deepcopy(container)
-            new_container.parent = self
-            new_container.name = field_name
-            new_tuple_fields.append(new_container)
+            container = copy.deepcopy(container)
+            container.parent = self
+            container.name = field_name
+            new_tuple_fields.append(container)
+
         self.tuple_fields = new_tuple_fields
 
     def _serialize(self, value, attr, obj, **kwargs):
@@ -1288,6 +1295,9 @@ def __init__(self, keys=None, values=None, **kwargs):
                     '"values" must be a subclass or instance of '
                     'marshmallow.base.FieldABC.',
                 )
+            if isinstance(self.value_container, Nested):
+                self.only = self.value_container.only
+                self.exclude = self.value_container.exclude
 
     def _bind_to_schema(self, field_name, schema):
         super()._bind_to_schema(field_name, schema)
@@ -1295,6 +1305,9 @@ def _bind_to_schema(self, field_name, schema):
             self.value_container = copy.deepcopy(self.value_container)
             self.value_container.parent = self
             self.value_container.name = field_name
+        if isinstance(self.value_container, Nested):
+            self.value_container.only = self.only
+            self.value_container.exclude = self.exclude
         if self.key_container:
             self.key_container = copy.deepcopy(self.key_container)
             self.key_container.parent = self


Test patch:
diff --git a/tests/test_fields.py b/tests/test_fields.py
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -247,6 +247,36 @@ class MySchema(Schema):
 
 class TestListNested:
 
+    @pytest.mark.parametrize('param', ('only', 'exclude', 'dump_only', 'load_only'))
+    def test_list_nested_only_exclude_dump_only_load_only_propagated_to_nested(self, param):
+
+        class Child(Schema):
+            name = fields.String()
+            age = fields.Integer()
+
+        class Family(Schema):
+            children = fields.List(fields.Nested(Child))
+
+        schema = Family(**{param: ['children.name']})
+        assert getattr(schema.fields['children'].container.schema, param) == {'name'}
+
+    @pytest.mark.parametrize(
+        ('param', 'expected'),
+        (('only', {'name'}), ('exclude', {'name', 'surname', 'age'})),
+    )
+    def test_list_nested_only_and_exclude_merged_with_nested(self, param, expected):
+
+        class Child(Schema):
+            name = fields.String()
+            surname = fields.String()
+            age = fields.Integer()
+
+        class Family(Schema):
+            children = fields.List(fields.Nested(Child, **{param: ('name', 'surname')}))
+
+        schema = Family(**{param: ['children.name', 'children.age']})
+        assert getattr(schema.fields['children'].container, param) == expected
+
     def test_list_nested_partial_propagated_to_nested(self):
 
         class Child(Schema):
@@ -279,6 +309,20 @@ class Family(Schema):
 
 class TestTupleNested:
 
+    @pytest.mark.parametrize('param', ('dump_only', 'load_only'))
+    def test_tuple_nested_only_exclude_dump_only_load_only_propagated_to_nested(self, param):
+
+        class Child(Schema):
+            name = fields.String()
+            age = fields.Integer()
+
+        class Family(Schema):
+            children = fields.Tuple((fields.Nested(Child), fields.Nested(Child)))
+
+        schema = Family(**{param: ['children.name']})
+        assert getattr(schema.fields['children'].tuple_fields[0].schema, param) == {'name'}
+        assert getattr(schema.fields['children'].tuple_fields[1].schema, param) == {'name'}
+
     def test_tuple_nested_partial_propagated_to_nested(self):
 
         class Child(Schema):
@@ -311,6 +355,36 @@ class Family(Schema):
 
 class TestDictNested:
 
+    @pytest.mark.parametrize('param', ('only', 'exclude', 'dump_only', 'load_only'))
+    def test_dict_nested_only_exclude_dump_only_load_only_propagated_to_nested(self, param):
+
+        class Child(Schema):
+            name = fields.String()
+            age = fields.Integer()
+
+        class Family(Schema):
+            children = fields.Dict(values=fields.Nested(Child))
+
+        schema = Family(**{param: ['children.name']})
+        assert getattr(schema.fields['children'].value_container.schema, param) == {'name'}
+
+    @pytest.mark.parametrize(
+        ('param', 'expected'),
+        (('only', {'name'}), ('exclude', {'name', 'surname', 'age'})),
+    )
+    def test_dict_nested_only_and_exclude_merged_with_nested(self, param, expected):
+
+        class Child(Schema):
+            name = fields.String()
+            surname = fields.String()
+            age = fields.Integer()
+
+        class Family(Schema):
+            children = fields.Dict(values=fields.Nested(Child, **{param: ('name', 'surname')}))
+
+        schema = Family(**{param: ['children.name', 'children.age']})
+        assert getattr(schema.fields['children'].value_container, param) == expected
+
     def test_dict_nested_partial_propagated_to_nested(self):
 
         class Child(Schema):