Skip to main content

Adds Injector support to Flask.

Project description

Adds Injector support to Flask.

Injector is a dependency-injection framework for Python, inspired by Guice.

This brings several benefits to Flask:

  • No need for a global “app” object, or globals in general. This makes testing simpler.

  • Explicit assignment of routes at app construction time.

  • Class-based routes with injected arguments.

Typical application layout

The first step is generally to create views. Views are global functions or classes marked with the @route decorator (t has the same arguments as Flask’s @app.route decorator). Views can have dependencies injected into them as keyword arguments by using the Injector.inject decorator:

import sqlite3
from flask.ext.injector import FlaskInjector, route
from flask import Config
from injector import inject

@route("/bar")
def bar():
    return render("bar.html")


# Route with injection
@route("/foo")
@inject(db=sqlite3.Connection)
def foo(db):
    users = db.execute('SELECT * FROM users').all()
    return render("foo.html")


# Class-based route with injected constructor
@route('/waz')
class Waz(object):
    @inject(db=sqlite3.Connection)
    def __init__(self, db):
        self.db = db

    @route("/waz/<key>")
    def waz(self, key):
        users = db.execute('SELECT * FROM users WHERE name=?', (key,)).all()
        return 'waz'

In the Injector world, all dependency configuration and initialization is performed in modules. The same is true with Flask-Injector. You can see some examples of configuring Flask extensions through modules below.

Accordingly, the next step is to create modules for any objects we want made available to the application. Note that in this example we also use the injector to gain access to the flask.Config, which is bound by FlaskInjector:

# Configure our SQLite connection object
@inject(config=Config)
def configure(binder, config):
    binder.bind(
        sqlite3.Connection,
        to=sqlite3.Connection(config['DB_CONNECTION_STRING']),
        scope=request,
        )

Instantiate the Flask instance in main():

def main():
    app = Flask(__name__)

Update the Flask app configuration as normal, additionally passing in any configuration for modules:

app.config.update(
    DB_CONNECTION_STRING=':memory:',
    )

Create a list of view functions and classes to install into the application:

views = [foo, bar, Waz]

Create a list of Injector modules to use for configuring the application state:

modules = [configure]

Construct a FlaskInjector instance, passing the view list and module list to the constructor, and initialize the application with it:

flask_injector = FlaskInjector(views, modules)
injector = flask_injector.init_app(app)

Run the Flask application as normal:

app.run()

See example.py for a more complete example, including Flask-SQLAlchemy and Flask-Cache integration.

Supporting Flask Extensions

Typically, Flask extensions are initialized at the global scope using a pattern similar to the following.

app = Flask(__name__)
ext = ExtClass(app)

@app.route(...)
def view():
    # Use ext object here...

As we don’t have these globals with Flask-Injector we have to configure the extension the Injector way - through modules. Modules can either be subclasses of injector.Module or a callable taking an injector.Binder instance.

@inject(app=Flask)
def configure_ext(binder, app):
    binder.bind(ExtClass, to=ExtClass(app), scope=singleton)

def main():
    app = Flask(__name__)
    app.config.update(
        EXT_CONFIG_VAR='some_value',
    )
    fi = FlaskInjector([view], [configure_ext])
    fi.init_app(app)
    app.run()

Make sure to bind extension objects as singletons.

Working Example 1: Flask-SQLAlchemy integration

This is a full working example of integrating Flask-SQLAlchemy.

We use standard SQLAlchemy models rather than the Flask-SQLAlchemy magic.

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String

Base = declarative_base()


class KeyValue(Base):
    __tablename__ = 'data'

    key = Column(String, primary_key=True)
    value = Column(String)

    def __init__(self, key, value):
        self.key = key
        self.value = value

And to register the Flask-SQLAlchemy extension.

from flast.ext.sqlalchemy import SQLAlchemy

class SQLAlchemyModule(Module):
    @inject(app=Flask)
    def configure(self, binder, app):
        db = self.configure_db(app)
        binder.bind(SQLAlchemy, to=db, scope=singleton)

    def configure_db(self, app):
        db = SQLAlchemy(app)
        Base.metadata.create_all(db.engine)
        db.session.add_all([
            KeyValue('hello', 'world'),
            KeyValue('goodbye', 'cruel world'),
        ])
        db.session.commit()
        return db

Working Example 2: Flask-Cache integration

class CacheModule(Module):
    """Configure the application."""
    @inject(app=Flask)
    def configure(self, binder, app):
        binder.bind(Cache, to=Cache(app), scope=singleton)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

Flask-Injector-0.2.1.tar.gz (6.4 kB view details)

Uploaded Source

File details

Details for the file Flask-Injector-0.2.1.tar.gz.

File metadata

File hashes

Hashes for Flask-Injector-0.2.1.tar.gz
Algorithm Hash digest
SHA256 43525b5984e3d01b36a6e894b096515a69204d1276817c6ce40fe556e91cbdd1
MD5 163ea546c44c05a007fa7a4dd5cca97f
BLAKE2b-256 6ca09aad6623ffa596960900ad005fabd526a62b1b8a430215fcfc4e04d13aa4

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page