-
-
Notifications
You must be signed in to change notification settings - Fork 629
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix propagation of options to Nested containers #1066
Conversation
Do we need to do the same thing with |
Should we also propagate |
Yes, probably. It could be worth factorizing this logic in a |
@lafrech Will you be doing the |
I'd like to do that while finishing this PR. It shouldn't take more time with the refactor than without. I just didn't take the time to work on the PR yet. |
OK, no problem. |
d05db56
to
aa3d082
Compare
I extended the fix to the The tests only test that the modifier is properly set on the container field, not the actual behaviour. Indeed, we should extend this to other modifier. However, it looks more complicated than I expected. At least, all params in
def __init__(
self, only=None, exclude=(), many=False, context=None,
load_only=(), dump_only=(), partial=False, unknown=None,
): Call to self.__schema = schema_class(
many=self.many,
only=self.only, exclude=self.exclude, context=context,
load_only=self._nested_normalized_option('load_only'),
dump_only=self._nested_normalized_option('dump_only'),
)
def _nested_normalized_option(self, option_name):
nested_field = '%s.' % self.name
return [field.split(nested_field, 1)[1]
for field in getattr(self.root, option_name, set())
if field.startswith(nested_field)] See, Some parameters are not passed to the nested def _load(self, value, data, partial=None):
try:
valid_data = self.schema.load(
value, unknown=self.unknown,
partial=partial,
)
And then there is the case of params such as I didn't spend much time digging into this, maybe there is a good reason, but from a quick look, it seems modifiers management deserves to be unified. It does not have to hold this PR. If the implementation is fine, we could merge this and open another issue to track work on what I wrote above. |
src/marshmallow/fields.py
Outdated
class ContainerMixin(object): | ||
"""Common methods for container fields""" | ||
|
||
def get_container_modifiers(self, container): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drive-by: this name is a bit misleading, since this method actually mutates self
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm not really satisfied with those method names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ContainerMixin.get_container_modifiers()
-> ContainerMixin.set_modifiers()
?
ContainerMixin.set_container_modifiers()
-> Nested.set_modifiers()
?
It seems like Nested
should be responsible for setting its own modifiers if other fields are. Making it a no-op on Field
and just defining it where needed would avoid the type check and allow propagation between containers (like List(List(Nested(S)))
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like a reasonable approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I pushed a new commit in which I tried to implement something along those lines. I still find it a bit awkward. But maybe I misunderstood your comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Jared is suggesting to only have set_modifiers
which container fields override.
class Field(...):
def set_modifiers(self):
# noop
class ContainerMixin(...):
def set_modifiers(self):
# sets modifiers on inner fields
I think you can remove get_modifiers
.
Since those are not passed at However, we need to make sure that all parameters that can be passed at Schema init are passed in |
Propagating |
src/marshmallow/fields.py
Outdated
attr: getattr(self, attr) for attr in CONTAINER_MODIFIERS}) | ||
|
||
|
||
class List(Field, ContainerMixin): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since Python's MRO goes from left to right, ContainerMixin
should come before Field
.
Just did a quick glance for now; can look more in-depth later. |
Once #1066 (comment) and #1066 (comment), this should be good to merge. |
598ad36
to
e48cf5a
Compare
Rebased. Fixed MRO issue. I still don't get #1066 (comment). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disregard #1066 (comment), actually. I wasn't fully understanding how this was working. I think your refactor in e48cf5a will suffice.
How about renaming ContainerMixin.get_container_modifiers()
to ContainerMixin.init_container_modifiers
?
After reviewing this, it occurs to me that the container
attribute is a misnomer (copy pasta from Flask-RESTful). We should probably rename it to something like inner
. That can happen in a different PR, though.
e48cf5a
to
9f4813d
Compare
Done.
Done. I'm still unsure about #1066 (comment). I'd like to be sure adding other modifiers wouldn't break the design before merging this. |
Great! Go ahead and merge whenever you feel confident in this. |
|
The issue with Since |
One way to solve the issue with # Bad
nested = Nested(Schema, only=...)
# Good
nested = Nested(Schema(only=...)) This respects the "only one way to do one thing" principle. But it prevents from passing modifiers to a schema passed as string. Perhaps just passing schema parameters in a dedicated dict. It doesn't look that good, but it would be required only for schemas passed as strings. nested_as_instance = Nested(Schema(only=...))
nested_as_string = Nested('Schema', nested_kwargs={'only':...}) Edit: In this comment and below, @deckar01 proposes just the opposite (only pass classes, not instances). In any case, having only one way to support parametrizing nested schemas would make things easier. |
I'm not satisfied with the Things we may want to do later on but are beyond the scope:
The two latter may be postponed to MA 4. |
This is absolutely wrong. There's a test case to prove it in #779 (comment). Draft PR: #1230 |
"container" is a misnomer #1066 (review)
Let's go with #1229 . |
"container" is a misnomer #1066 (review)
Fixes #946.
With
Dict
, I figured the nested options would apply to values schema, not keys schema.