Browse Source

Modify extensionsdev documentation.

pull/204/head
Matt Dawson 14 years ago
parent
commit
c1c20ac108
  1. 129
      docs/extensiondev.rst

129
docs/extensiondev.rst

@ -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_sqlite3(app): def init_app(self, app):
app = app self.app = app
app.config.setdefault('SQLITE3_DATABASE', ':memory:') self.app.config.setdefault('SQLITE3_DATABASE', ':memory:')
self.app.after_request(self.after_request)
self.app.before_request(self.before_request)
def connect(self):
return sqlite3.connect(app.config['SQLITE3_DATABASE'])
@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
------------------- -------------------

Loading…
Cancel
Save