python-restx / flask-restx

Fork of Flask-RESTPlus: Fully featured framework for fast, easy and documented API development with Flask
https://flask-restx.readthedocs.io/en/latest/
Other
2.16k stars 335 forks source link

Models nested inside response fields aren't registered #613

Open panda-byte opened 2 months ago

panda-byte commented 2 months ago

Code

from flask import Flask
from flask_restx import Resource, Api, fields

app = Flask(__name__)
api = Api(app)

model = api.model('Model', {'foo': fields.String})

# @api.route('/get-one')
# @api.doc(model=model)
# class One(Resource):
#     def get(self):
#         return {'foo': "bar"}

@api.route('/get-many')
@api.doc(model=fields.List(fields.Nested(model)))
class Many(Resource):
    def get(self):
        return [{'foo': "bar"}]

if __name__ == '__main__':
    app.run(debug=True)

Expected Behavior

The Swagger doc page should show the get-many endpoint with Model used for the response documentation. Model should appear in the list of models at the end.

Actual Behavior

Model is not included in the list of models, and when the endpoint is expanded, an error is displayed:

Resolver error at paths./get-many.get.responses.200.schema.items.$ref
Could not resolve reference: Could not resolve pointer: /definitions/Model does not exist in document

Environment

Additional Context

If the get-one endpoint is uncommented, the behavior is as expected. The cause of the error is in serialize_schema in swagger.py, which gets called to serialize the documentation for the model argument. When a model is passed, it is correctly registered, as well as any nested models. However, if a fields.List(fields.Nested(model)) is passed like in the example, the nested model isn't included. The easiest fix is to include a call to register_field(model) for fields, which is also called to detect models nested inside other models. I will open a pull request with the proposed solution.

A similar problem goes for the body parameter of api.doc, which documents the expected data (corresponding to api.expect). However, the arguments allowed for model aren't all covered by body. For example, model allows to specify models via their name as a string, while body doesn't. This problem of inconsistent handling is touched upon in #56.

A workaround is to use RESTX_INCLUDE_ALL_MODELS, which includes all defined models in the Swagger doc, regardless of actual usage.