jmcarp / flask-apispec

MIT License
655 stars 156 forks source link

Using the API documentation feature in blueprints #12

Open fabian-marquardt opened 8 years ago

fabian-marquardt commented 8 years ago

I have a project where a REST API is defined inside a blueprint so that the flask application may be shipped with or without the API. To create the FlaskApiSpec, you have to pass the Flask app object like this:

docs = FlaskApiSpec(app)

However, if I am inside the blueprint, I do not have access to the app object. As a workaround, I have defined a decorator which saves all the API views to a dictionary for later registration in the main app context:

flask_api = Blueprint('flask_api', __name__, template_folder='templates')
flask_api.API_VIEWS = {}

def apidoc_lazy_register(endpoint_name):
    def _apidoc_lazy_register(view_function):
        flask_api.API_VIEWS[endpoint_name] = view_function

        @wraps(view_function)
        def lazy_register_function(*args, **kwargs):
            return view_function(*args, **kwargs)
        return lazy_register_function
    return _apidoc_lazy_register
app.register_blueprint(flask_api, url_prefix='/api')
for key, value in flask_api.API_VIEWS.items():
    docs.register(value, endpoint=key, blueprint='flask_api')

This works, but is certainly not a nice approach. Would it instead be possible to create a FlaskApiSpec object directly within the context of a blueprint? Or is there any other solution to do this in a nicer way?

thclark commented 8 years ago

(not tested, just thinking out loud)... Inside the blueprint you could use flask's current_app() to get the current app?

from flask import current_app
app = current_app()
docs = FlaskApiSpec(app)
fabian-marquardt commented 8 years ago

The flask current_app function works only when inside an application context (i.e. inside a view function):

>>> from flask import current_app
>>> app = current_app()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/rumpf/venv_default/lib/python3.5/site-packages/werkzeug/local.py", line 371, in <lambda>
    __call__ = lambda x, *a, **kw: x._get_current_object()(*a, **kw)
  File "/home/rumpf/venv_default/lib/python3.5/site-packages/werkzeug/local.py", line 302, in _get_current_object
    return self.__local()
  File "/home/rumpf/venv_default/lib/python3.5/site-packages/flask/globals.py", line 34, in _find_app
    raise RuntimeError('working outside of application context')
RuntimeError: working outside of application context
thclark commented 8 years ago

Ach, my bad - apologies, @fabian-rump

lafrech commented 8 years ago

I'm using blueprints as well. I patched flask-apispec to add an init_app method: https://github.com/jmcarp/flask-apispec/issues/21.

I can't import app in my blueprint, but I can import docs, which is a FlaskApiSpec object.

I guess FlaskApiSpec could be modifed to

Just a thought. I'm not sure it is relevant. @fabian-rump, what do you think?

fabian-marquardt commented 8 years ago

Sounds like a suitable solution to me. However I should note that currently I have switched over to apispec.