Users are often bit by the fact that fields store arbitrary keyword arguments as metadata. See https://github.com/marshmallow-code/marshmallow/issues/683.
...The reasons we use **kwargs instead of e.g.
metadata=are mostly historical. The original decision was that storing kwargs 1) was more concise and 2) saved us from having to come up with an appropriate name... "metadata" didn't seem right because there are use cases where the things your storing aren't really metadata. At this point, it's not worth breaking the API.
Not the best reasons, but I think it's not terrible. We've discussed adding a whitelist of metadata keys in the past, but we decided it wasn't worth the added API surface.
_Originally posted by @sloria in https://github.com/marshmallow-code/marshmallow/issues/779#issuecomment-522283135_
Possible solutions:
metadata=.Feedback welcome!
Solution 1. is preferable to 2., I think. That said, there are some use cases where it's awkward to call additional kwargs "metadata". location in webargs is one that comes to mind.
# current API
"some_query_param": fields.Bool(location="query")
though we could probably wrap all the fields in webargs to take the additional location argument. 馃
I wanted to note that even webargs' location doesn't necessarily make the case against metadata=.... I was surprised/confused at first when I went looking for location in marshmallow and found no mention of it. At the cost of a little bit of verbosity, it would make it easier to understand how marshmallow is functioning.
Relatedly, the plan in https://github.com/marshmallow-code/webargs/issues/419 includes making location=... for webargs a thing of the past.
cc @jtrakk . This was your suggestion in https://github.com/marshmallow-code/marshmallow/issues/779#issuecomment-522282845 . I'm leaning towards this more and more.
+1 on this, IMHO biggest problem of self.metadata=kwargs is not that it's unexpected, but that it's not generating errors on wrong keyword arguments, which is pretty annoying due frequent api changes :-) so - you can find mistakes only later, all your typos in metadata field...
One one hand, I think it is better to specify metadata=. More explicit.
OTOH, this will make my models a bit more verbose:
class MyModel(ma.Schema:
some_int = ma.fields.Int(
required=True,
validate=ma.validate.OneOf([1, 2, 3]),
metadata={"description": "This string explains what this is all about"}
)
For such a use case, the shortcut of using extra kwargs as metadata is nice.
If we went with solution 2, users would be able to extend the whitelist with their own stuff. Apispec could extend it with the keyword arguments it expects (valid OpenAPI attributes) and we could even catch typos inside metadata, while solution 1 blindly accepts anything in metadata.
However, this would prevent accepting arbitrary attributes in metadata, which sucks. E.g. in apispec, we also accept any "x-..." attribute. So we'd need to genericize the whitelist to a callable mechanism. And we end up with a gas factory feature while we wanted to make thing simple.
Overall, perhaps the downside of 1 (model verbosity) is not that bad.
Agreed. Consider this accepted.
My plan is to deprecate metadata=kwargs in a later 3.x release. Let's let the dust settle on v3 before bombarding users with DeprecationWarnings 馃槄
As I was integrating apispec into one of our large codebases at work, I found I really wanted to be able to start using metadata={"type": "date-time"}. Especially with some of the openapi names which come up in apispec, it's hard to clarify that some of those keys (format, type, pattern) are not actually part of how a field evaluates.
I'd like to be able to start using an explicit metadata parameter today, under 3.x, if we think it's okay to make that change.
I've opened a draft PR with an initial cut at this change to help figure out if it's safe/okay to do.
This will make specifying metadata in marshmallow-dataclass very awkward. We will have to write things like
py
@dataclass
class C:
my_attr: float = field(metadata={"metadata": {"description": "my description"}})
That issue is rather specific to the marshmallow-dataclass, and I think the benefits still outweigh the cost. It is much better to have a proper exception get raised when a user passes an invalid keyword argument rather than passing silently.
Most helpful comment
That issue is rather specific to the marshmallow-dataclass, and I think the benefits still outweigh the cost. It is much better to have a proper exception get raised when a user passes an invalid keyword argument rather than passing silently.