Regression.
Flask 0.10.1, Flask-RESTful 0.2.11 (from PyPI)
from flask import Flask
from flask.ext.restful import reqparse, abort, Api, Resource
class Test(Resource):
def get(self):
return {'foo': 'bar'}
app = Flask(__name__)
# This one returns 404
api = Api()
api.init_app(app)
# This works okay
# api = Api(app)
api.add_resource(Test, '/test/')
if __name__ == '__main__':
app.run(debug=True)
> curl http://127.0.0.1:5000/test/
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
Here: https://github.com/twilio/flask-restful/commit/a1d59b76c788f1d895003f7d95e5563776549059#diff-2d0ae3f4d6c2de850684766cfb7c5a6bR83
you should add_resource first, then init_app .
api = Api()
api.add_resource(Test, '/test/')
api.init_app(app)
That's a bit weird and need some refactoring.
_init_app() and dead code if app is not None: next to self.app = NoneFlask-kit:
def get_app(self, app_module_name, **kwargs):
self.app = Flask(app_module_name, **kwargs)
self.app.config.from_object(self.app_config)
self._generate_db_mapping()
self._bind_extensions()
self._register_blueprints()
self._register_context_processors()
My other app written with some boilerplate:
def app_factory(config, app_name=None, blueprints=None):
app_name = app_name or __name__
app = Flask(app_name)
config = config_str_to_obj(config)
configure_app(app, config)
configure_database(app)
configure_cache(app)
configure_blueprints(app, blueprints or config.BLUEPRINTS)
configure_error_handlers(app)
configure_extensions(app)
configure_context_processors(app)
configure_template_filters(app)
configure_before_request(app)
configure_views(app)
return app
Had the same issue. What is misleading are the explanations:
Initialize this class with the given :class:`flask.Flask`
application or :class:`flask.Blueprint` object.
:param app: the Flask application or blueprint object
:type app: flask.Flask
:type app: flask.Blueprint
Examples::
api = Api()
api.init_app(app)
api.add_resource(...)
If you look at the code of the add_ressource function:
if self.app is not None:
self._register_view(self.app, resource, *urls, **kwargs)
else:
self.resources.append((resource, urls, kwargs))
adding a ressource is self.app is not defined add it to a list-like object and when you call init_app, you get
try:
app.record(self._deferred_blueprint_init)
# Flask.Blueprint has a 'record' attribute, Flask.Api does not
except AttributeError:
self._init_app(app)
else:
self.blueprint = app
While I don't know what the app.record does, there's a call to the self._init_app(app) which register the views:
if len(self.resources) > 0:
for resource, urls, kwargs in self.resources:
self._register_view(app, resource, *urls, **kwargs)
So basically, for it to works, you have to do the following
api = Api()
api.add_resource(...)
api.init_app(app)
Which does make sense somehow as you delay the initialization but can register api views within the views itself at imports time and not only, exclusively in a separate function when you create you app objects. So, it's not really a bug in my opinion but rather a documentation issue :)
Sorry about this guys. I've updated the documentation to match working behavior:
api = Api()
api.add_resource(...)
api.init_app(app)
Based on the discussion here that sounds like the correct fix so I'm going to go ahead and close this out. If you still feel this isn't fixed, feel free to reopen and we can talk about solutions.
Most helpful comment
you should add_resource first, then init_app .