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

Adding description to fields.Nested makes it display as String field #479

Open AFlowOfCode opened 2 years ago

AFlowOfCode commented 2 years ago

This occurs in the Swagger visualization under the endpoint. In the Models section it is always correctly displayed as an object, but note that when there is a description it seems to lose the model name as well. When there is no description it correctly displays NestedObj next to the arrow which expands the nested model.

Since Nested inherits from Raw, I would expect description to be a valid keyword argument & be initialized in this line: super(Nested, self).__init__(**kwargs). This seems to be a bug, or if I'm doing something wrong, at minimum it is confusing and unexpected behavior.

nested_obj = api.model('NestedObj', {
    'nested_field': fields.String()    
})

parent_obj = api.model('ParentObj', {
    'parent_field': fields.String(),
    'nested_obj': fields.Nested(nested_obj, required=True, description="A nested object")
})

nested description converts display to string nested description converts display to string 2

nested_obj = api.model('NestedObj', {
    'nested_field': fields.String()    
})

parent_obj = api.model('ParentObj', {
    'parent_field': fields.String(),
    'nested_obj': fields.Nested(nested_obj, required=True)
})

nested description converts display to string (without description) nested description converts display to string (without description) 2

peter-doggart commented 2 years ago

Which method are you using to document the models on your endpoints?

When I take your first batch of code into a sample app, I get the expected result.

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

app = Flask(__name__)
api = Api(app, version='1.0', title='Test API',
    description='A simple Test API',
)

ns = api.namespace('test', description='Test operations')

nested_obj = api.model('NestedObj', {
    'nested_field': fields.String()    
})

parent_obj = api.model('ParentObj', {
    'parent_field': fields.String(),
    'nested_obj': fields.Nested(nested_obj, required=True, description="A nested object")
})

@ns.route('/')
class Test(Resource):
    '''Test'''
    @ns.expect(parent_obj)
    def post(self):
        '''Create a new task'''
        return parent_obj, 201

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

Screenshot from 2022-10-07 10-52-43

AFlowOfCode commented 2 years ago

I had it like this:

@ns.route('/')
@ns.expect(parent_obj)
class Test(Resource):
    '''Test'''
    def post(self):
        '''Create a new task'''
        return parent_obj, 201

But thanks to your example I discovered when I put the decorator directly above the endpoint definition vs the class it does work as expected.

Is it specifically incorrect to put it above the class? Generally speaking putting the decorator above the class always works, except in this instance apparently.

peter-doggart commented 2 years ago

Is it specifically incorrect to put it above the class?

@AFlowOfCode No, as far as I'm aware, both should work the same, so this is definitely a bug! Thanks for providing how to reproduce, I will try and find some time to take a look / fix. It definitely has to be a small difference in how the swagger.json file is being populated somewhere!

AFlowOfCode commented 2 years ago

Thanks & thanks for demonstrating the workaround in the meantime.