Search code examples
pythonrestflaskblueprintflask-restful

Blueprint and factory pattern work together?


I am working on a restful service using flask-restful, and I want to leverage both factory pattern and blueprint in my project. in app/__init__.py I have a create_app function to create a flask app and return it to outside caller, so the caller can start the app.

def create_app():
    app = Flask(__name__)
    app.config.from_object('app.appconfig.DevelopmentConfig')
    from app.resource import resource
    app.register_blueprint(v1, url_prefix='/api')
    print app.url_map
    return app

Inside that function I intended to register a blueprint pointing to the implementation package with a prefix url.

In app/resource/__init__.py there is the following code:

from flask import current_app, Blueprint, render_template
from flask.ext import restful
resource = Blueprint('resource', __name__, url_prefix='/api')

@resource.route('/')
def index():    
    api = restful.Api(current_app)
    from resource.HelloWorld import HelloWorld
    api.add_resource(HelloWorld, '/hello')

My goal is that I can access the HelloWorld rest service at url /api/hello, but I know the above code has something wrong at part of @resource.route('/') .... I got some error like AssertionError: A setup function was called after the first request was handled. This usually indicates a bug in the app ... at api.add_resource(HelloWorld, '/hello').
Could you please give me some hints on the correct approach?


Solution

  • Flask-Restful, like all properly implemented Flask extensions, supports two methods of registering itself:

    1. With the app at instantiation (as you are trying to do with Api(current_app))
    2. At a later point using api.init_app(app)

    The canonical way of dealing with the circular imports issue is to use the second pattern and import the instantiated extension in your create_app function and register the extension using the init_app method:

    # app/resource/__init__.py
    from resource.hello_world import HelloWorld
    
    api = restful.Api(prefix='/api/v1')  # Note, no app
    api.add_resource(HelloWorld, '/hello')
    
    # We could actually register our API on a blueprint
    # and then import and register the blueprint as normal
    # but that's an alternate we will leave for another day
    # bp = Blueprint('resource', __name__, url_prefix='/api')
    # api.init_app(bp)
    

    And then in your create_app call you would simply load and register the api:

    def create_app():
        # ... snip ...
        # We import our extension
        # and register it with our application
        # without any circular references
        # Our handlers can use `current_app`, as you already know
        from app.resource import api
        api.init_app(app)