3

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']
            }
        }
Arul
  • 143
  • 3
  • 12

2 Answers2

0

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 ;)

0

It's nearly the same as @Jeremie's answer, but just would like to point to the official doc.

There's following statements in flask-restx.

Flask-RESTX provides a way to use almost the same pattern as Flask’s blueprint. The main idea is to split your app into reusable namespaces.

And comes with an example:

# let's say the file name is `cat_controller`.
from flask_restx import Namespace, Resource, fields

api = Namespace('cats', description='Cats related operations')

cat = api.model('Cat', {
    'id': fields.String(required=True, description='The cat identifier'),
    'name': fields.String(required=True, description='The cat name'),
})

CATS = [
    {'id': 'felix', 'name': 'Felix'},
]

@api.route('/')
class CatList(Resource):
    @api.doc('list_cats')
    @api.marshal_list_with(cat)
    def get(self):
        '''List all cats'''
        return CATS

And we need add_namespace():

from flask import Flask
from flask_restx import Api
from .cat_controller import api as ns1
from .dog_controller import api as ns2
# ...

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

api.add_namespace(ns1)
api.add_namespace(ns2)

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

mCY
  • 2,731
  • 7
  • 25
  • 43