|
|
@ -137,7 +137,6 @@ Now this is where your extension code goes. But how exactly should such |
|
|
|
an extension look like? What are the best practices? Continue reading |
|
|
|
an extension look like? What are the best practices? Continue reading |
|
|
|
for some insight. |
|
|
|
for some insight. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Initializing Extensions |
|
|
|
Initializing Extensions |
|
|
|
----------------------- |
|
|
|
----------------------- |
|
|
|
|
|
|
|
|
|
|
@ -165,8 +164,8 @@ classes: |
|
|
|
a remote application that uses OAuth. |
|
|
|
a remote application that uses OAuth. |
|
|
|
|
|
|
|
|
|
|
|
What to use depends on what you have in mind. For the SQLite 3 extension |
|
|
|
What to use depends on what you have in mind. For the SQLite 3 extension |
|
|
|
we will need to use the class based approach because we have to use a |
|
|
|
we will use the class based approach because it will provide users with a |
|
|
|
controller object that can be used to connect to the database. |
|
|
|
manager object that handles opening and closing database connections. |
|
|
|
|
|
|
|
|
|
|
|
The Extension Code |
|
|
|
The Extension Code |
|
|
|
------------------ |
|
|
|
------------------ |
|
|
@ -175,87 +174,124 @@ Here's the contents of the `flaskext/sqlite3.py` for copy/paste:: |
|
|
|
|
|
|
|
|
|
|
|
from __future__ import absolute_import |
|
|
|
from __future__ import absolute_import |
|
|
|
import sqlite3 |
|
|
|
import sqlite3 |
|
|
|
from flask import g |
|
|
|
|
|
|
|
|
|
|
|
from flask import _request_ctx_stack |
|
|
|
|
|
|
|
|
|
|
|
class SQLite3(object): |
|
|
|
class SQLite3(object): |
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, app): |
|
|
|
def __init__(self, app): |
|
|
|
self.app = app |
|
|
|
self.app = app |
|
|
|
self.app.config.setdefault('SQLITE3_DATABASE', ':memory:') |
|
|
|
self.app.config.setdefault('SQLITE3_DATABASE', ':memory:') |
|
|
|
|
|
|
|
|
|
|
|
self.app.before_request(self.before_request) |
|
|
|
|
|
|
|
self.app.after_request(self.after_request) |
|
|
|
self.app.after_request(self.after_request) |
|
|
|
|
|
|
|
self.app.before_request(self.before_request) |
|
|
|
|
|
|
|
|
|
|
|
def connect(self): |
|
|
|
def connect(self): |
|
|
|
return sqlite3.connect(self.app.config['SQLITE3_DATABASE']) |
|
|
|
return sqlite3.connect(self.app.config['SQLITE3_DATABASE']) |
|
|
|
|
|
|
|
|
|
|
|
def before_request(self): |
|
|
|
def before_request(self): |
|
|
|
g.sqlite3_db = self.connect() |
|
|
|
ctx = _request_ctx_stack.top |
|
|
|
|
|
|
|
ctx.sqlite3_db = self.connect() |
|
|
|
|
|
|
|
|
|
|
|
def after_request(self, response): |
|
|
|
def after_request(self, response): |
|
|
|
g.sqlite3_db.close() |
|
|
|
ctx = _request_ctx_stack.top |
|
|
|
|
|
|
|
ctx.sqlite3_db.close() |
|
|
|
return response |
|
|
|
return response |
|
|
|
|
|
|
|
|
|
|
|
So here's what the lines of code do: |
|
|
|
def get_db(self): |
|
|
|
|
|
|
|
ctx = _request_ctx_stack.top |
|
|
|
1. the ``__future__`` import is necessary to activate absolute imports. |
|
|
|
if ctx is not None: |
|
|
|
This is needed because otherwise we could not call our module |
|
|
|
return ctx.sqlite3_db |
|
|
|
`sqlite3.py` and import the top-level `sqlite3` module which actually |
|
|
|
|
|
|
|
implements the connection to SQLite. |
|
|
|
So here's what these lines of code do: |
|
|
|
2. We create a class for our extension that sets a default configuration |
|
|
|
|
|
|
|
for the SQLite 3 database if it's not there (:meth:`dict.setdefault`) |
|
|
|
1. The ``__future__`` import is necessary to activate absolute imports. |
|
|
|
and connects two functions as before and after request handlers. |
|
|
|
Otherwise we could not call our module `sqlite3.py` and import the |
|
|
|
3. Then it implements a `connect` function that returns a new database |
|
|
|
top-level `sqlite3` module which actually implements the connection to |
|
|
|
connection and the two handlers. |
|
|
|
SQLite. |
|
|
|
|
|
|
|
2. We create a class for our extension that requires a supplied `app` object, |
|
|
|
So why did we decide on a class based approach here? Because using that |
|
|
|
sets a configuration for the database if it's not there |
|
|
|
|
|
|
|
(:meth:`dict.setdefault`), and attaches `before_request` and |
|
|
|
|
|
|
|
`after_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:: |
|
|
|
extension looks something like this:: |
|
|
|
|
|
|
|
|
|
|
|
from flask import Flask, g |
|
|
|
from flask import Flask |
|
|
|
from flaskext.sqlite3 import SQLite3 |
|
|
|
from flaskext.sqlite3 import SQLite3 |
|
|
|
|
|
|
|
|
|
|
|
app = Flask(__name__) |
|
|
|
app = Flask(__name__) |
|
|
|
app.config.from_pyfile('the-config.cfg') |
|
|
|
app.config.from_pyfile('the-config.cfg') |
|
|
|
db = SQLite(app) |
|
|
|
manager = SQLite3(app) |
|
|
|
|
|
|
|
db = manager.get_db() |
|
|
|
|
|
|
|
|
|
|
|
Either way you can use the database from the views like this:: |
|
|
|
You can then use the database from views like this:: |
|
|
|
|
|
|
|
|
|
|
|
@app.route('/') |
|
|
|
@app.route('/') |
|
|
|
def show_all(): |
|
|
|
def show_all(): |
|
|
|
cur = g.sqlite3_db.cursor() |
|
|
|
cur = db.cursor() |
|
|
|
cur.execute(...) |
|
|
|
cur.execute(...) |
|
|
|
|
|
|
|
|
|
|
|
But how would you open a database connection from outside a view function? |
|
|
|
Opening a database connection from outside a view function is simple. |
|
|
|
This is where the `db` object now comes into play: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>>> from yourapplication import db |
|
|
|
>>> from yourapplication import db |
|
|
|
>>> con = db.connect() |
|
|
|
>>> cur = db.cursor() |
|
|
|
>>> cur = con.cursor() |
|
|
|
>>> cur.execute(...) |
|
|
|
|
|
|
|
|
|
|
|
If you don't need that, you can go with initialization functions. |
|
|
|
Adding an `init_app` Function |
|
|
|
|
|
|
|
----------------------------- |
|
|
|
|
|
|
|
|
|
|
|
Initialization Functions |
|
|
|
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:: |
|
|
|
|
|
|
|
|
|
|
|
Here's what the module would look like with initialization functions:: |
|
|
|
class SQLite3(object): |
|
|
|
|
|
|
|
|
|
|
|
from __future__ import absolute_import |
|
|
|
def __init__(self, app=None): |
|
|
|
import sqlite3 |
|
|
|
if app is not None: |
|
|
|
from flask import g |
|
|
|
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.after_request(self.after_request) |
|
|
|
|
|
|
|
self.app.before_request(self.before_request) |
|
|
|
|
|
|
|
|
|
|
|
def init_sqlite3(app): |
|
|
|
def connect(self): |
|
|
|
app = app |
|
|
|
return sqlite3.connect(app.config['SQLITE3_DATABASE']) |
|
|
|
app.config.setdefault('SQLITE3_DATABASE', ':memory:') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.before_request |
|
|
|
def before_request(self): |
|
|
|
def before_request(): |
|
|
|
ctx = _request_ctx_stack.top |
|
|
|
g.sqlite3_db = sqlite3.connect(self.app.config['SQLITE3_DATABASE']) |
|
|
|
ctx.sqlite3_db = self.connect() |
|
|
|
|
|
|
|
|
|
|
|
@app.after_request |
|
|
|
def after_request(self, response): |
|
|
|
def after_request(response): |
|
|
|
ctx = _request_ctx_stack.top |
|
|
|
g.sqlite3_db.close() |
|
|
|
ctx.sqlite3_db.close() |
|
|
|
return response |
|
|
|
return response |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_db(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() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
and bind their app to the extension in another file:: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
manager.init_app(app) |
|
|
|
|
|
|
|
|
|
|
|
Learn from Others |
|
|
|
Learn from Others |
|
|
|
----------------- |
|
|
|
----------------- |
|
|
|
|
|
|
|
|
|
|
@ -276,7 +312,6 @@ designing the API. |
|
|
|
The best Flask extensions are extensions that share common idioms for the |
|
|
|
The best Flask extensions are extensions that share common idioms for the |
|
|
|
API. And this can only work if collaboration happens early. |
|
|
|
API. And this can only work if collaboration happens early. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Approved Extensions |
|
|
|
Approved Extensions |
|
|
|
------------------- |
|
|
|
------------------- |
|
|
|
|
|
|
|
|
|
|
|