|
|
|
@ -125,9 +125,8 @@ Initializing Extensions
|
|
|
|
|
----------------------- |
|
|
|
|
|
|
|
|
|
Many extensions will need some kind of initialization step. For example, |
|
|
|
|
consider your application is currently connecting to SQLite like the |
|
|
|
|
documentation suggests (:ref:`sqlite3`) you will need to provide a few |
|
|
|
|
functions and before / after request handlers. So how does the extension |
|
|
|
|
consider an application that's currently connecting to SQLite like the |
|
|
|
|
documentation suggests (:ref:`sqlite3`). So how does the extension |
|
|
|
|
know the name of the application object? |
|
|
|
|
|
|
|
|
|
Quite simple: you pass it to it. |
|
|
|
@ -135,12 +134,14 @@ Quite simple: you pass it to it.
|
|
|
|
|
There are two recommended ways for an extension to initialize: |
|
|
|
|
|
|
|
|
|
initialization functions: |
|
|
|
|
|
|
|
|
|
If your extension is called `helloworld` you might have a function |
|
|
|
|
called ``init_helloworld(app[, extra_args])`` that initializes the |
|
|
|
|
extension for that application. It could attach before / after |
|
|
|
|
handlers etc. |
|
|
|
|
|
|
|
|
|
classes: |
|
|
|
|
|
|
|
|
|
Classes work mostly like initialization functions but can later be |
|
|
|
|
used to further change the behaviour. For an example look at how the |
|
|
|
|
`OAuth extension`_ works: there is an `OAuth` object that provides |
|
|
|
@ -148,22 +149,29 @@ classes:
|
|
|
|
|
a remote application that uses OAuth. |
|
|
|
|
|
|
|
|
|
What to use depends on what you have in mind. For the SQLite 3 extension |
|
|
|
|
we will use the class-based approach because it will provide users with a |
|
|
|
|
manager object that handles opening and closing database connections. |
|
|
|
|
we will use the class-based approach because it will provide users with an |
|
|
|
|
object that handles opening and closing database connections. |
|
|
|
|
|
|
|
|
|
The Extension Code |
|
|
|
|
------------------ |
|
|
|
|
|
|
|
|
|
Here's the contents of the `flask_sqlite3.py` for copy/paste:: |
|
|
|
|
|
|
|
|
|
from __future__ import absolute_import |
|
|
|
|
import sqlite3 |
|
|
|
|
|
|
|
|
|
from flask import _request_ctx_stack |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SQLite3(object): |
|
|
|
|
|
|
|
|
|
def __init__(self, app): |
|
|
|
|
def __init__(self, app=None): |
|
|
|
|
if app is not None: |
|
|
|
|
self.app = app |
|
|
|
|
self.init_app(self.app) |
|
|
|
|
else: |
|
|
|
|
self.app = None |
|
|
|
|
|
|
|
|
|
def init_app(self, app): |
|
|
|
|
self.app = app |
|
|
|
|
self.app.config.setdefault('SQLITE3_DATABASE', ':memory:') |
|
|
|
|
self.app.teardown_request(self.teardown_request) |
|
|
|
@ -180,27 +188,29 @@ Here's the contents of the `flask_sqlite3.py` for copy/paste::
|
|
|
|
|
ctx = _request_ctx_stack.top |
|
|
|
|
ctx.sqlite3_db.close() |
|
|
|
|
|
|
|
|
|
def get_db(self): |
|
|
|
|
@property |
|
|
|
|
def connection(self): |
|
|
|
|
ctx = _request_ctx_stack.top |
|
|
|
|
if ctx is not None: |
|
|
|
|
return ctx.sqlite3_db |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
So here's what these lines of code do: |
|
|
|
|
|
|
|
|
|
1. The ``__future__`` import is necessary to activate absolute imports. |
|
|
|
|
Otherwise we could not call our module `sqlite3.py` and import the |
|
|
|
|
top-level `sqlite3` module which actually implements the connection to |
|
|
|
|
SQLite. |
|
|
|
|
2. We create a class for our extension that requires a supplied `app` object, |
|
|
|
|
sets a configuration for the database if it's not there |
|
|
|
|
(:meth:`dict.setdefault`), and attaches `before_request` and |
|
|
|
|
`teardown_request` handlers. |
|
|
|
|
3. Next, we define a `connect` function that opens a database connection. |
|
|
|
|
1. The ``__init__`` method takes an optional app object and, if supplied, will |
|
|
|
|
call ``init_app``. |
|
|
|
|
2. The ``init_app`` method exists so that the ``SQLite3`` object can be |
|
|
|
|
instantiated without requiring an app object. This method supports the |
|
|
|
|
factory pattern for creating applications. The ``init_app`` will set the |
|
|
|
|
configuration for the database, defaulting to an in memory database if |
|
|
|
|
no configuration is supplied. In addition, the ``init_app`` method attaches |
|
|
|
|
``before_request`` and ``teardown_request`` handlers. |
|
|
|
|
3. Next, we define a ``connect`` method that opens a database connection. |
|
|
|
|
4. Then we set up the request handlers we bound to the app above. Note here |
|
|
|
|
that we're attaching our database connection to the top request context via |
|
|
|
|
`_request_ctx_stack.top`. Extensions should use the top context and not the |
|
|
|
|
`g` object to store things like database connections. |
|
|
|
|
5. Finally, we add a `get_db` function that simplifies access to the context's |
|
|
|
|
``_request_ctx_stack.top``. Extensions should use the top context and not the |
|
|
|
|
``g`` object to store things like database connections. |
|
|
|
|
5. Finally, we add a ``connection`` property that simplifies access to the context's |
|
|
|
|
database. |
|
|
|
|
|
|
|
|
|
So why did we decide on a class-based approach here? Because using our |
|
|
|
@ -211,68 +221,36 @@ extension looks something like this::
|
|
|
|
|
|
|
|
|
|
app = Flask(__name__) |
|
|
|
|
app.config.from_pyfile('the-config.cfg') |
|
|
|
|
manager = SQLite3(app) |
|
|
|
|
db = manager.get_db() |
|
|
|
|
db = SQLite3(app) |
|
|
|
|
|
|
|
|
|
You can then use the database from views like this:: |
|
|
|
|
|
|
|
|
|
@app.route('/') |
|
|
|
|
def show_all(): |
|
|
|
|
cur = db.cursor() |
|
|
|
|
cur = db.connection.cursor() |
|
|
|
|
cur.execute(...) |
|
|
|
|
|
|
|
|
|
Opening a database connection from outside a view function is simple. |
|
|
|
|
|
|
|
|
|
>>> from yourapplication import db |
|
|
|
|
>>> cur = db.cursor() |
|
|
|
|
>>> cur.execute(...) |
|
|
|
|
|
|
|
|
|
Adding an `init_app` Function |
|
|
|
|
----------------------------- |
|
|
|
|
|
|
|
|
|
In practice, you'll almost always want to permit users to initialize your |
|
|
|
|
extension and provide an app object after the fact. This can help avoid |
|
|
|
|
circular import problems when a user is breaking their app into multiple files. |
|
|
|
|
Our extension could add an `init_app` function as follows:: |
|
|
|
|
|
|
|
|
|
class SQLite3(object): |
|
|
|
|
|
|
|
|
|
def __init__(self, app=None): |
|
|
|
|
if app is not None: |
|
|
|
|
self.app = app |
|
|
|
|
self.init_app(self.app) |
|
|
|
|
else: |
|
|
|
|
self.app = None |
|
|
|
|
|
|
|
|
|
def init_app(self, app): |
|
|
|
|
self.app = app |
|
|
|
|
self.app.config.setdefault('SQLITE3_DATABASE', ':memory:') |
|
|
|
|
self.app.teardown_request(self.teardown_request) |
|
|
|
|
self.app.before_request(self.before_request) |
|
|
|
|
|
|
|
|
|
def connect(self): |
|
|
|
|
return sqlite3.connect(app.config['SQLITE3_DATABASE']) |
|
|
|
|
|
|
|
|
|
def before_request(self): |
|
|
|
|
ctx = _request_ctx_stack.top |
|
|
|
|
ctx.sqlite3_db = self.connect() |
|
|
|
|
Additionally, the ``init_app`` method is used to support the factory pattern |
|
|
|
|
for creating apps:: |
|
|
|
|
|
|
|
|
|
def teardown_request(self, exception): |
|
|
|
|
ctx = _request_ctx_stack.top |
|
|
|
|
ctx.sqlite3_db.close() |
|
|
|
|
db = Sqlite3() |
|
|
|
|
# Then later on. |
|
|
|
|
app = create_app('the-config.cfg') |
|
|
|
|
db.init_app(app) |
|
|
|
|
|
|
|
|
|
def get_db(self): |
|
|
|
|
ctx = _request_ctx_stack.top |
|
|
|
|
if ctx is not None: |
|
|
|
|
return ctx.sqlite3_db |
|
|
|
|
Keep in mind that supporting this factory pattern for creating apps is required |
|
|
|
|
for approved flask extensions (described below). |
|
|
|
|
|
|
|
|
|
The user could then initialize the extension in one file:: |
|
|
|
|
|
|
|
|
|
manager = SQLite3() |
|
|
|
|
Using _request_ctx_stack |
|
|
|
|
------------------------ |
|
|
|
|
|
|
|
|
|
and bind their app to the extension in another file:: |
|
|
|
|
|
|
|
|
|
manager.init_app(app) |
|
|
|
|
In the example above, before every request, a ``sqlite3_db`` variable is assigned |
|
|
|
|
to ``_request_ctx_stack.top``. In a view function, this variable is accessible |
|
|
|
|
using the ``connection`` property of ``SQLite3``. During the teardown of a |
|
|
|
|
request, the ``sqlite3_db`` connection is closed. By using this pattern, the |
|
|
|
|
*same* connection to the sqlite3 database is accessible to anything that needs it |
|
|
|
|
for the duration of the request. |
|
|
|
|
|
|
|
|
|
End-Of-Request Behavior |
|
|
|
|
----------------------- |
|
|
|
@ -292,6 +270,7 @@ pattern is a good way to support both::
|
|
|
|
|
else: |
|
|
|
|
app.after_request(close_connection) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Strictly speaking the above code is wrong, because teardown functions are |
|
|
|
|
passed the exception and typically don't return anything. However because |
|
|
|
|
the return value is discarded this will just work assuming that the code |
|
|
|
|