Flask-restful: Delayed initialization — init_app() not working

Created on 26 Feb 2014  Â·  5Comments  Â·  Source: flask-restful/flask-restful

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

bug

Most helpful comment

you should add_resource first, then init_app .

api = Api()
api.add_resource(Test, '/test/')
api.init_app(app)

All 5 comments

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.

  1. http://flask-restful.readthedocs.org/en/latest/api.html?highlight=init_app#flask.ext.restful.Api.init_app
  2. Factory actually worked before factory-fix :)
  3. Inexplicit initialization with _init_app() and dead code if app is not None: next to self.app = None
  4. If you're using factory pattern then brobably all app-related stuff are initialized before main logic, views etc.

Flask-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.

Was this page helpful?
0 / 5 - 0 ratings