python flask-restful blueprint and factory pattern work together?

jeffrey picture jeffrey · Feb 12, 2014 · Viewed 7.4k times · Source

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 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 pls give me some hints on correct approach ? Thanks!

Answer

Sean Vieira picture Sean Vieira · Feb 12, 2014

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)