forked from orbit-oss/flask
Reword the docs for writing a flask extension
There was a minor bug in the example extension that's been fixed. I also updated the description of the fixed code accordingly, and expanded on the usage of _request_ctx_stack.top for adding data that should be accesible to view functions. I verified that the existing code as is works as expected.
This commit is contained in:
parent
7ed3cba658
commit
8d1546f8e6
1 changed files with 64 additions and 85 deletions
|
|
@ -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,92 +149,18 @@ 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):
|
||||
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(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 get_db(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.
|
||||
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
|
||||
database.
|
||||
|
||||
So why did we decide on a class-based approach here? Because using our
|
||||
extension looks something like this::
|
||||
|
||||
from flask import Flask
|
||||
from flask_sqlite3 import SQLite3
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_pyfile('the-config.cfg')
|
||||
manager = SQLite3(app)
|
||||
db = manager.get_db()
|
||||
|
||||
You can then use the database from views like this::
|
||||
|
||||
@app.route('/')
|
||||
def show_all():
|
||||
cur = db.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):
|
||||
|
||||
|
|
@ -251,7 +178,7 @@ Our extension could add an `init_app` function as follows::
|
|||
self.app.before_request(self.before_request)
|
||||
|
||||
def connect(self):
|
||||
return sqlite3.connect(app.config['SQLITE3_DATABASE'])
|
||||
return sqlite3.connect(self.app.config['SQLITE3_DATABASE'])
|
||||
|
||||
def before_request(self):
|
||||
ctx = _request_ctx_stack.top
|
||||
|
|
@ -261,18 +188,69 @@ Our extension could add an `init_app` function as follows::
|
|||
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
|
||||
|
||||
The user could then initialize the extension in one file::
|
||||
|
||||
manager = SQLite3()
|
||||
So here's what these lines of code do:
|
||||
|
||||
and bind their app to the extension in another file::
|
||||
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 ``connection`` property that simplifies access to the context's
|
||||
database.
|
||||
|
||||
manager.init_app(app)
|
||||
So why did we decide on a class-based approach here? Because using our
|
||||
extension looks something like this::
|
||||
|
||||
from flask import Flask
|
||||
from flask_sqlite3 import SQLite3
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_pyfile('the-config.cfg')
|
||||
db = SQLite3(app)
|
||||
|
||||
You can then use the database from views like this::
|
||||
|
||||
@app.route('/')
|
||||
def show_all():
|
||||
cur = db.connection.cursor()
|
||||
cur.execute(...)
|
||||
|
||||
Additionally, the ``init_app`` method is used to support the factory pattern
|
||||
for creating apps::
|
||||
|
||||
db = Sqlite3()
|
||||
# Then later on.
|
||||
app = create_app('the-config.cfg')
|
||||
db.init_app(app)
|
||||
|
||||
Keep in mind that supporting this factory pattern for creating apps is required
|
||||
for approved flask extensions (described below).
|
||||
|
||||
|
||||
Using _request_ctx_stack
|
||||
------------------------
|
||||
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue