Added documentation for appcontext and teardown handlers

This commit is contained in:
Armin Ronacher 2012-04-09 17:29:00 +01:00
parent 34bbd3100b
commit 9bed20c07c
10 changed files with 287 additions and 64 deletions

View file

@ -1,3 +1,5 @@
.. _extension-dev:
Flask Extension Development
===========================
@ -152,6 +154,11 @@ 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 an
object that handles opening and closing database connections.
What's important about classes is that they encourage to be shared around
on module level. In that case, the object itself must not under any
circumstances store any application specific state and must be shareable
between different application.
The Extension Code
------------------
@ -159,7 +166,13 @@ Here's the contents of the `flask_sqlite3.py` for copy/paste::
import sqlite3
from flask import _request_ctx_stack
# Find the stack on which we want to store the database connection.
# Starting with Flask 0.9, the _app_ctx_stack is the correct one,
# before that we need to use the _request_ctx_stack.
try:
from flask import _app_ctx_stack as stack
except ImportError:
from flask import _request_ctx_stack as stack
class SQLite3(object):
@ -172,26 +185,28 @@ Here's the contents of the `flask_sqlite3.py` for copy/paste::
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)
app.config.setdefault('SQLITE3_DATABASE', ':memory:')
# Use the newstyle teardown_appcontext if it's available,
# otherwise fall back to the request context
if hasattr(app, 'teardown_appcontext'):
app.teardown_appcontext(self.teardown)
else:
app.teardown_request(self.teardown)
def connect(self):
return sqlite3.connect(self.app.config['SQLITE3_DATABASE'])
def before_request(self):
ctx = _request_ctx_stack.top
ctx.sqlite3_db = self.connect()
def teardown_request(self, exception):
ctx = _request_ctx_stack.top
ctx.sqlite3_db.close()
def teardown(self, exception):
ctx = stack.top
if hasattr(ctx, 'sqlite3_db'):
ctx.sqlite3_db.close()
@property
def connection(self):
ctx = _request_ctx_stack.top
ctx = stack.top
if ctx is not None:
if not hasattr(ctx, 'sqlite3_db'):
ctx.sqlite3_db = self.connect()
return ctx.sqlite3_db
@ -204,14 +219,19 @@ So here's what these lines of code do:
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.
the ``teardown`` handler. It will try to use the newstyle app context
handler and if it does not exist, falls back to the request context
one.
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 ``connection`` property that simplifies access to the context's
database.
4. Finally, we add a ``connection`` property that on first access opens
the database connection and stores it on the context.
Note here that we're attaching our database connection to the top
application context via ``_app_ctx_stack.top``. Extensions should use
the top context for storing their own information with a sufficiently
complex name. Note that we're falling back to the
``_request_ctx_stack.top`` if the application is using an older
version of Flask that does not support it.
So why did we decide on a class-based approach here? Because using our
extension looks something like this::
@ -241,19 +261,38 @@ for creating apps::
Keep in mind that supporting this factory pattern for creating apps is required
for approved flask extensions (described below).
.. admonition:: Note on ``init_app``
Using _request_ctx_stack
------------------------
As you noticed, ``init_app`` does not assign ``app`` to ``self``. This
is intentional! Class based Flask extensions must only store the
application on the object when the application was passed to the
constructor. This tells the extension: I am not interested in using
multiple applications.
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.
When the extension needs to find the current application and it does
not have a reference to it, it must either use the
:data:`~flask.current_app` context local or change the API in a way
that you can pass the application explicitly.
End-Of-Request Behavior
-----------------------
Using _app_ctx_stack
--------------------
In the example above, before every request, a ``sqlite3_db`` variable is
assigned to ``_app_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.
If the :data:`~flask._app_ctx_stack` does not exist because the user uses
an old version of Flask, it is recommended to fall back to
:data:`~flask._request_ctx_stack` which is bound to a request.
Teardown Behavior
-----------------
*This is only relevant if you want to support Flask 0.6 and older*
Due to the change in Flask 0.7 regarding functions that are run at the end
of the request your extension will have to be extra careful there if it
@ -270,7 +309,6 @@ 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