Repository: marshmallow-code/marshmallow

Issue:
Nested partial not working as expected
When using partial in Nested field it does not work.
Problem is because Nested gets partial from parent class that is False as default parameter on schema constructor.

So the Nested class gets partial=False in deserialize:
https://github.com/marshmallow-code/marshmallow/blob/dev/src/marshmallow/fields.py#L673

because in parent you have:
```
        if partial is None:
            partial = self.partial
```

and self.partial is set in constructor with default False.

This used to work with marshmallow 2.

```
from marshmallow import Schema
from marshmallow import fields
from marshmallow import validate


class IbanWhitelistSchema(Schema):
    id = fields.Int(required=True)
    user_id = fields.Int(required=True)
    user_id__in = fields.List(fields.Int(required=True))

    auto_withdrawal_bank_account_id = fields.Int(required=True, allow_none=True)
    deposit_bank_account_id = fields.Int(required=True, allow_none=True)

    internal_id = fields.Str(validate=validate.Length(max=36))
    external_id = fields.Str(validate=validate.Length(max=255), allow_none=True)
    payment_method_external_id = fields.UUID(allow_none=True)

    withdrawal_limit_amount = fields.Decimal(allow_none=True)


class IbanWhitelistCreateSchema(Schema):
    iban_data = fields.Nested(
        IbanWhitelistSchema(
            partial=[
                'bic',
                'intermed_bic',
                'external_id',
                'payment_method_external_id',
                'auto_withdrawal_bank_account_id',
                'deposit_bank_account_id',
                'account_type',
                'withdrawal_limit_amount',
            ],
            exclude=['id', 'user_id__in'],
        ),
        required=True,
    )
    changed_by_id = fields.Int(required=True)


schema = IbanWhitelistCreateSchema()
schema2 = IbanWhitelistSchema(
    partial=[
        'external_id',
        'payment_method_external_id',
        'auto_withdrawal_bank_account_id',
        'deposit_bank_account_id',
        'withdrawal_limit_amount',
    ],
    exclude=['id', 'user_id__in'],
)

iban_data = {
        'user_id': 4186806,
        'external_id': 'wp5rJy31NMfyEJNDvoqXhbwLlRaEGyCrwWWdy',
        'payment_method_external_id': 'c30e82b3-cc13-4692-b140-1042411bd093',
    }

data = {
    'iban_data': iban_data,
    'changed_by_id': 4186806,
}

schema2.load(iban_data)
schema.load(data)
```

So first schema2.load(iban_data) works as expected
The second fail with (but shouldnt't because it is marked as partial class)
```
marshmallow.exceptions.ValidationError: {'iban_data': {'auto_withdrawal_bank_account_id': ['Missing data for required field.'], 'deposit_bank_account_id': ['Missing data for required field.']}}
```



Discussion:

Patch:
diff --git a/src/marshmallow/schema.py b/src/marshmallow/schema.py
index cde8c080..4e7fd828 100644
--- a/src/marshmallow/schema.py
+++ b/src/marshmallow/schema.py
@@ -374,7 +374,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
         context: dict | None = None,
         load_only: types.StrSequenceOrSet = (),
         dump_only: types.StrSequenceOrSet = (),
-        partial: bool | types.StrSequenceOrSet = False,
+        partial: bool | types.StrSequenceOrSet | None = None,
         unknown: str | None = None,
     ):
         # Raise error if only or exclude is passed as string, not list of strings
@@ -590,7 +590,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
         *,
         error_store: ErrorStore,
         many: bool = False,
-        partial=False,
+        partial=None,
         unknown=RAISE,
         index=None,
     ) -> _T | list[_T]:
@@ -657,7 +657,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
                         f[len_prefix:] for f in partial if f.startswith(prefix)
                     ]
                     d_kwargs["partial"] = sub_partial
-                else:
+                elif partial is not None:
                     d_kwargs["partial"] = partial
 
                 def getter(
@@ -1089,7 +1089,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
         *,
         many: bool,
         original_data,
-        partial: bool | types.StrSequenceOrSet,
+        partial: bool | types.StrSequenceOrSet | None,
     ):
         # This has to invert the order of the dump processors, so run the pass_many
         # processors first.
@@ -1166,7 +1166,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
         data,
         original_data,
         many: bool,
-        partial: bool | types.StrSequenceOrSet,
+        partial: bool | types.StrSequenceOrSet | None,
         field_errors: bool = False,
     ):
         for attr_name in self._hooks[(VALIDATES_SCHEMA, pass_many)]:


Test patch:
diff --git a/tests/test_deserialization.py b/tests/test_deserialization.py
index 6a2c7a87..29cab67b 100644
--- a/tests/test_deserialization.py
+++ b/tests/test_deserialization.py
@@ -2236,6 +2236,21 @@ class TestValidation:
         with pytest.raises(ValidationError):
             SchemaB().load(b_dict, partial=("z.y",))
 
+    def test_nested_partial_default(self):
+        class SchemaA(Schema):
+            x = fields.Integer(required=True)
+            y = fields.Integer(required=True)
+
+        class SchemaB(Schema):
+            z = fields.Nested(SchemaA(partial=("x",)))
+
+        b_dict = {"z": {"y": 42}}
+        # Nested partial args should be respected.
+        result = SchemaB().load(b_dict)
+        assert result["z"]["y"] == 42
+        with pytest.raises(ValidationError):
+            SchemaB().load({"z": {"x": 0}})
+
 
 @pytest.mark.parametrize("FieldClass", ALL_FIELDS)
 def test_required_field_failure(FieldClass):  # noqa