pythonflask-restx

Flask-Restx Swagger Doc issue


I am getting AttributeError: module 'flask_restx.api' has no attribute 'doc' when it try add additional for API when using flask restx. How this error can be fixed.

api.py

from flask import Flask
from flask_restx import Api, Resource
from hello import Hello

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

api.add_resource(Hello, '/hello')

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

hello.py

from flask_restx import Resource, api


@api.doc(params={'id': 'An ID'})
class Hello(Resource):
    def get(self):
        return {
            'data': {
                'names': ['one',
                          'two',
                          'three']
            }
        }

Solution

  • I don't know which tutorial you followed (if you followed any), but the

    @api.doc(params={'id': 'An ID'})
    

    needs an instance of Api class and not flask-restx.api

    Usually, in tutorials (at least the ones I found), they show how to do all of that in the same file. So your code would work if it was written like so :

    api.py

    from flask import Flask
    from flask_restx import Api, Resource
    from hello import Hello
    
    app = Flask(__name__)
    api = Api(app)
    
    @api.doc(params={'id': 'An ID'})
    class Hello(Resource):
        def get(self):
            return {
                'data': {
                    'names': ['one',
                              'two',
                              'three']
                }
            }
    
    api.add_resource(Hello, '/hello')
    
    if __name__ == '__main__':
        app.run(debug=True)
    


    Now, this is not what you want to achieve, as I guess you would like to split the file to have some structure (as I would like to as well). Awfully, I couldn't find a proper tutorial for that online, but here is what I did in my project (using your example code):

    api > __init__.py

    from flask_restx import Namespace
    
    default_namespace = Namespace("default", ordered=True)
    

    api > hello.py

    from flask_restx import Resource
    from api import default_namespace as ns
    
    
    @ns.doc(params={'id': 'An ID'})
    class Hello(Resource):
        def get(self):
            return {
                'data': {
                    'names': ['one',
                              'two',
                              'three']
                }
            }
    

    app.py (your api.py file) located at root

    from flask import Flask
    from flask_restx import Api
    from hello import Hello
    from api import default_namespace
    
    app = Flask(__name__)
    api = Api(app)
    
    api.add_resource(Hello, '/hello')
    api.add_namespace(default_namespace)
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    Where the package structure is:

    .
    +-- app.py
    +-- api
       +-- __init__.py
       +-- hello.py
    

    By declaring a namespace, you can actually group Resources together in Swagger. And the reason why I put the namespace in another file (here __init__.py) is to not have a circular import. And you can use all decorators with ns.doc, ns.route, ... as you could with the api. (where api is a variable ;) ). Btw, the ns is an Alias, if you prefer to put something else, it is up to you to change it in the import statement.

    Note that this might not be the best way, but it is clean enough for me and the structure I have. If someone with more experience knows how to do this differently, please reply ;)