@@ -137,7 +137,6 @@ Now this is where your extension code goes. But how exactly should such
137
137
an extension look like? What are the best practices? Continue reading
138
138
for some insight.
139
139
140
-
141
140
Initializing Extensions
142
141
-----------------------
143
142
@@ -165,8 +164,8 @@ classes:
165
164
a remote application that uses OAuth.
166
165
167
166
What to use depends on what you have in mind. For the SQLite 3 extension
168
- we will need to use the class based approach because we have to use a
169
- controller object that can be used to connect to the database.
167
+ we will use the class based approach because it will provide users with a
168
+ manager object that handles opening and closing database connections .
170
169
171
170
The Extension Code
172
171
------------------
@@ -175,87 +174,124 @@ Here's the contents of the `flaskext/sqlite3.py` for copy/paste::
175
174
176
175
from __future__ import absolute_import
177
176
import sqlite3
178
- from flask import g
177
+
178
+ from flask import _request_ctx_stack
179
179
180
180
class SQLite3(object):
181
-
181
+
182
182
def __init__(self, app):
183
183
self.app = app
184
184
self.app.config.setdefault('SQLITE3_DATABASE', ':memory:')
185
-
186
- self.app.before_request(self.before_request)
187
185
self.app.after_request(self.after_request)
186
+ self.app.before_request(self.before_request)
188
187
189
188
def connect(self):
190
189
return sqlite3.connect(self.app.config['SQLITE3_DATABASE'])
191
190
192
191
def before_request(self):
193
- g.sqlite3_db = self.connect()
192
+ ctx = _request_ctx_stack.top
193
+ ctx.sqlite3_db = self.connect()
194
194
195
195
def after_request(self, response):
196
- g.sqlite3_db.close()
196
+ ctx = _request_ctx_stack.top
197
+ ctx.sqlite3_db.close()
197
198
return response
198
199
199
- So here's what the lines of code do:
200
-
201
- 1. the ``__future__ `` import is necessary to activate absolute imports.
202
- This is needed because otherwise we could not call our module
203
- `sqlite3.py ` and import the top-level `sqlite3 ` module which actually
204
- implements the connection to SQLite.
205
- 2. We create a class for our extension that sets a default configuration
206
- for the SQLite 3 database if it's not there (:meth: `dict.setdefault `)
207
- and connects two functions as before and after request handlers.
208
- 3. Then it implements a `connect ` function that returns a new database
209
- connection and the two handlers.
210
-
211
- So why did we decide on a class based approach here? Because using that
200
+ def get_db(self):
201
+ ctx = _request_ctx_stack.top
202
+ if ctx is not None:
203
+ return ctx.sqlite3_db
204
+
205
+ So here's what these lines of code do:
206
+
207
+ 1. The ``__future__ `` import is necessary to activate absolute imports.
208
+ Otherwise we could not call our module `sqlite3.py ` and import the
209
+ top-level `sqlite3 ` module which actually implements the connection to
210
+ SQLite.
211
+ 2. We create a class for our extension that requires a supplied `app ` object,
212
+ sets a configuration for the database if it's not there
213
+ (:meth: `dict.setdefault `), and attaches `before_request ` and
214
+ `after_request ` handlers.
215
+ 3. Next, we define a `connect ` function that opens a database connection.
216
+ 4. Then we set up the request handlers we bound to the app above. Note here
217
+ that we're attaching our database connection to the top request context via
218
+ `_request_ctx_stack.top `. Extensions should use the top context and not the
219
+ `g ` object to store things like database connections.
220
+ 5. Finally, we add a `get_db ` function that simplifies access to the context's
221
+ database.
222
+
223
+ So why did we decide on a class based approach here? Because using our
212
224
extension looks something like this::
213
225
214
- from flask import Flask, g
226
+ from flask import Flask
215
227
from flaskext.sqlite3 import SQLite3
216
228
217
229
app = Flask(__name__)
218
230
app.config.from_pyfile('the-config.cfg')
219
- db = SQLite(app)
231
+ manager = SQLite3(app)
232
+ db = manager.get_db()
220
233
221
- Either way you can use the database from the views like this::
234
+ You can then use the database from views like this::
222
235
223
236
@app.route('/')
224
237
def show_all():
225
- cur = g.sqlite3_db .cursor()
238
+ cur = db .cursor()
226
239
cur.execute(...)
227
240
228
- But how would you open a database connection from outside a view function?
229
- This is where the `db ` object now comes into play:
241
+ Opening a database connection from outside a view function is simple.
230
242
231
243
>>> from yourapplication import db
232
- >>> con = db.connect ()
233
- >>> cur = con.cursor( )
244
+ >>> cur = db.cursor ()
245
+ >>> cur.execute( ... )
234
246
235
- If you don't need that, you can go with initialization functions.
247
+ Adding an `init_app ` Function
248
+ -----------------------------
236
249
237
- Initialization Functions
238
- ------------------------
250
+ In practice, you'll almost always want to permit users to initialize your
251
+ extension and provide an app object after the fact. This can help avoid
252
+ circular import problems when a user is breaking their app into multiple files.
253
+ Our extension could add an `init_app ` function as follows::
239
254
240
- Here's what the module would look like with initialization functions: :
255
+ class SQLite3(object) :
241
256
242
- from __future__ import absolute_import
243
- import sqlite3
244
- from flask import g
257
+ def __init__(self, app=None):
258
+ if app is not None:
259
+ self.app = app
260
+ self.init_app(self.app)
261
+ else:
262
+ self.app = None
245
263
246
- def init_sqlite3(app):
247
- app = app
248
- app.config.setdefault('SQLITE3_DATABASE', ':memory:')
264
+ def init_app(self, app):
265
+ self.app = app
266
+ self.app.config.setdefault('SQLITE3_DATABASE', ':memory:')
267
+ self.app.after_request(self.after_request)
268
+ self.app.before_request(self.before_request)
269
+
270
+ def connect(self):
271
+ return sqlite3.connect(app.config['SQLITE3_DATABASE'])
249
272
250
- @app. before_request
251
- def before_request():
252
- g .sqlite3_db = sqlite3 .connect(self.app.config['SQLITE3_DATABASE'] )
273
+ def before_request(self):
274
+ ctx = _request_ctx_stack.top
275
+ ctx .sqlite3_db = self .connect()
253
276
254
- @app. after_request
255
- def after_request(response):
256
- g .sqlite3_db.close()
277
+ def after_request(self, response):
278
+ ctx = _request_ctx_stack.top
279
+ ctx .sqlite3_db.close()
257
280
return response
258
281
282
+ def get_db(self):
283
+ ctx = _request_ctx_stack.top
284
+ if ctx is not None:
285
+ return ctx.sqlite3_db
286
+
287
+ The user could then initialize the extension in one file::
288
+
289
+ manager = SQLite3()
290
+
291
+ and bind their app to the extension in another file::
292
+
293
+ manager.init_app(app)
294
+
259
295
Learn from Others
260
296
-----------------
261
297
@@ -276,7 +312,6 @@ designing the API.
276
312
The best Flask extensions are extensions that share common idioms for the
277
313
API. And this can only work if collaboration happens early.
278
314
279
-
280
315
Approved Extensions
281
316
-------------------
282
317
0 commit comments