flask_accepts
flask_accepts copied to clipboard
Nested schemas with many=true do not generate a list of schemas in the swagger docs
Hi, thanks for taking the time to make this!
I found a corner case where the generated swagger docs / model do not match the Marshmallow schema provided. For nested fields, the parameter many=True
seems to be ignored when creating the model. For example, this schema:
class WidgetSchema(Schema):
bar = fields.String()
class GadgetSchema(Schema):
foo = fields.String()
widgets = fields.Nested(WidgetSchema, many=True)
appears in the generated swagger docs as:
{
"foo": "string",
"widgets": {
"bar": "string"
}
}
whereas the model should be:
{
"widgets": [
{
"bar": "string"
}
],
"foo": "string"
}
Here's a minimal reproduction of the problem based on the example in the readme...
from dataclasses import dataclass
from marshmallow import Schema, fields, post_load
from flask import Flask
from flask_accepts import responds
from flask_restx import Api, Resource
@dataclass
class Widget:
bar: str
@dataclass
class Gadget:
foo: str
widgets: list
class WidgetSchema(Schema):
bar = fields.String()
class GadgetSchema(Schema):
foo = fields.String()
widgets = fields.Nested(WidgetSchema, many=True)
def create_app():
app = Flask(__name__)
api = Api(app)
@api.route("/gadget")
class GadgetResource(Resource):
@responds(schema=GadgetSchema, api=api)
def get(self):
return Gadget(foo=None, widgets=[{'bar': None}, {'bar': None}])
return app
if __name__ == "__main__":
create_app().run()
Can confirm. We have this issue as well.
Hi, thank you for creating this neat library. I also have the same problem. Is there some kind of fix available by now? Enjoy your day!
I tried to overcome this by using fields.List but then the swagger documentation fails to build. I narrowed it down to
'test': {
'items': {
'example': <marshmallow.missing>,
'type': 'number'
}
where marshmallow.utils._Missing seems to end up in the dict before the json.dumps. Any idea where this is coming from? Seems to be more of an flask-restx issue, doesn't it?
It seems kinda hacky but I could resolve the fields.List issue by adding a type-check for ma.utils._Missing within the _ma_field_to_fr_field function. Any ideas about this? Where is this coming from?
def _ma_field_to_fr_field(value: ma.Field) -> dict:
fr_field_parameters = {}
# add type-checking for ma.utils._Missing
if hasattr(value, "default") and not isinstance(value.default, ma.utils._Missing):
fr_field_parameters["example"] = value.default
if hasattr(value, "required"):
fr_field_parameters["required"] = value.required
if hasattr(value, "metadata") and "description" in value.metadata:
fr_field_parameters["description"] = value.metadata["description"]
if hasattr(value, "missing") and type(value.missing) != ma.utils._Missing:
fr_field_parameters["default"] = value.missing
return fr_field_parameters
I have the feeling this is somehow related to issue #103 .
Apologies for delays responding here. I would happily review/merge a PR (w/ tests). I unfortunately cannot commit to a reasonable timeframe for addressing this myself due to personal time conflicts.
Thank's for responding. I'll see what I can do. :)