Skip to content

Commit b012877

Browse files
author
Jacques Fournier
committed
feat: many consistency between nested schema
1 parent 17873ee commit b012877

File tree

2 files changed

+55
-4
lines changed

2 files changed

+55
-4
lines changed

src/marshmallow/fields.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,9 @@ class ParentSchema(Schema):
482482
:param only: A list or tuple of fields to marshal. If `None`, all fields are marshalled.
483483
This parameter takes precedence over ``exclude``.
484484
:param many: Whether the field is a collection of objects.
485+
If `None` (default), and nested `Schema` instance is provided, ``many`` is not overridden.
486+
If `None` (default), and `Schema` subclass is provided, schema instance sets ``many`` as False.
487+
If `True | False` nested `[nested_field].schema.many` is overridden.
485488
:param unknown: Whether to exclude, include, or raise an error for unknown
486489
fields in the data. Use `EXCLUDE`, `INCLUDE` or `RAISE`.
487490
:param kwargs: The same keyword arguments that :class:`Field` receives.
@@ -502,7 +505,7 @@ def __init__(
502505
*,
503506
only: types.StrSequenceOrSet | None = None,
504507
exclude: types.StrSequenceOrSet = (),
505-
many: bool = False,
508+
many: bool | None = None,
506509
unknown: types.UnknownOption | None = None,
507510
**kwargs: Unpack[_BaseFieldKwargs],
508511
):
@@ -537,7 +540,7 @@ def schema(self) -> Schema:
537540

538541
if isinstance(nested, Schema):
539542
self._schema = copy.copy(nested)
540-
# Respect only and exclude passed from parent and re-initialize fields
543+
# Respect only and exclude and many passed from parent and re-initialize fields
541544
set_class = typing.cast("type[set]", self._schema.set_class)
542545
if self.only is not None:
543546
if self._schema.only is not None:
@@ -548,6 +551,8 @@ def schema(self) -> Schema:
548551
if self.exclude:
549552
original = self._schema.exclude
550553
self._schema.exclude = set_class(self.exclude) | set_class(original)
554+
if self.many is not None:
555+
self._schema.many = self.many
551556
self._schema._init_fields()
552557
else:
553558
if isinstance(nested, type) and issubclass(nested, Schema):
@@ -560,7 +565,7 @@ def schema(self) -> Schema:
560565
else:
561566
schema_class = class_registry.get_class(nested, all=False) # type: ignore[unreachable]
562567
self._schema = schema_class(
563-
many=self.many,
568+
many=bool(self.many),
564569
only=self.only,
565570
exclude=self.exclude,
566571
load_only=self._nested_normalized_option("load_only"),

tests/test_schema.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import math
33
import random
44
from collections import OrderedDict
5-
from typing import NamedTuple
5+
from typing import NamedTuple, cast
66

77
import pytest
88
import simplejson as json
@@ -1003,13 +1003,59 @@ class UserSchema(Schema):
10031003
books = [{"id": 1, "title": "First book"}, {"id": 2, "title": "Second book"}]
10041004
user = {"id": 1, "name": "Peter", "books": books}
10051005

1006+
assert cast("fields.Nested", UserSchema().fields["books"]).schema.many
1007+
1008+
user_dump = UserSchema().dump(user)
1009+
assert user_dump["books"] == books
1010+
1011+
user_load = UserSchema().load(user_dump)
1012+
assert user_load == user
1013+
1014+
1015+
def test_nested_many_should_override_schema_many_case1():
1016+
class BookSchema(Schema):
1017+
id = fields.Int()
1018+
title = fields.String()
1019+
1020+
class UserSchema(Schema):
1021+
id = fields.Int()
1022+
name = fields.String()
1023+
books = fields.Nested(BookSchema(), many=True)
1024+
1025+
books = [{"id": 1, "title": "First book"}, {"id": 2, "title": "Second book"}]
1026+
user = {"id": 1, "name": "Peter", "books": books}
1027+
1028+
assert cast("fields.Nested", UserSchema().fields["books"]).schema.many
1029+
10061030
user_dump = UserSchema().dump(user)
10071031
assert user_dump["books"] == books
10081032

10091033
user_load = UserSchema().load(user_dump)
10101034
assert user_load == user
10111035

10121036

1037+
def test_nested_many_should_override_schema_many_case2():
1038+
class BookSchema(Schema):
1039+
id = fields.Int()
1040+
title = fields.String()
1041+
1042+
class UserSchema(Schema):
1043+
id = fields.Int()
1044+
name = fields.String()
1045+
book = fields.Nested(BookSchema(many=True), many=False)
1046+
1047+
book = {"id": 1, "title": "First book"}
1048+
user = {"id": 1, "name": "Peter", "book": book}
1049+
1050+
assert not cast("fields.Nested", UserSchema().fields["book"]).schema.many
1051+
1052+
user_dump = UserSchema().dump(user)
1053+
assert user_dump["book"] == book
1054+
1055+
user_load = UserSchema().load(user_dump)
1056+
assert user_load == user
1057+
1058+
10131059
def test_nested_instance_only():
10141060
class ArtistSchema(Schema):
10151061
first = fields.Str()

0 commit comments

Comments
 (0)