Skip to content

Commit 05161d3

Browse files
committed
Rewrote tutorial to use the g based appcontext object
1 parent 42cf782 commit 05161d3

File tree

7 files changed

+162
-126
lines changed

7 files changed

+162
-126
lines changed

docs/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ following links:
2424
- `Jinja2 Documentation <http://jinja.pocoo.org/2/documentation/>`_
2525
- `Werkzeug Documentation <http://werkzeug.pocoo.org/documentation/>`_
2626

27+
2728
.. _Jinja2: http://jinja.pocoo.org/2/
2829
.. _Werkzeug: http://werkzeug.pocoo.org/
2930

docs/tutorial/dbcon.rst

+61-45
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,72 @@
11
.. _tutorial-dbcon:
22

3-
Step 4: Request Database Connections
4-
------------------------------------
5-
6-
Now we know how we can open database connections and use them for scripts,
7-
but how can we elegantly do that for requests? We will need the database
8-
connection in all our functions so it makes sense to initialize them
9-
before each request and shut them down afterwards.
10-
11-
Flask allows us to do that with the :meth:`~flask.Flask.before_request`,
12-
:meth:`~flask.Flask.after_request` and :meth:`~flask.Flask.teardown_request`
13-
decorators::
14-
15-
@app.before_request
16-
def before_request():
17-
g.db = connect_db()
18-
19-
@app.teardown_request
20-
def teardown_request(exception):
21-
db = getattr(g, 'db', None)
22-
if db is not None:
23-
db.close()
24-
25-
Functions marked with :meth:`~flask.Flask.before_request` are called before
26-
a request and passed no arguments. Functions marked with
27-
:meth:`~flask.Flask.after_request` are called after a request and
28-
passed the response that will be sent to the client. They have to return
29-
that response object or a different one. They are however not guaranteed
30-
to be executed if an exception is raised, this is where functions marked with
31-
:meth:`~flask.Flask.teardown_request` come in. They get called after the
32-
response has been constructed. They are not allowed to modify the request, and
33-
their return values are ignored. If an exception occurred while the request was
34-
being processed, it is passed to each function; otherwise, `None` is passed in.
35-
36-
We store our current database connection on the special :data:`~flask.g`
37-
object that Flask provides for us. This object stores information for one
38-
request only and is available from within each function. Never store such
39-
things on other objects because this would not work with threaded
40-
environments. That special :data:`~flask.g` object does some magic behind
41-
the scenes to ensure it does the right thing.
42-
43-
For an even better way to handle such resources see the :ref:`sqlite3`
44-
documentation.
45-
46-
Continue to :ref:`tutorial-views`.
3+
Step 3: Database Connections
4+
----------------------------
5+
6+
We have created a function for establishing a database connection with
7+
`create_db` but by itself that's not particularly useful. Creating and
8+
closing database connections all the time is very inefficient, so we want
9+
to keep it around for longer. Because database connections encapsulate a
10+
transaction we also need to make sure that only one request at the time
11+
uses the connection. So how can we elegantly do that with Flask?
12+
13+
This is where the application context comes into play. So let's start
14+
there.
15+
16+
Flask provides us with two contexts: the application context and the
17+
request context. For the time being all you have to know is that there
18+
are special variables that use these. For instance the
19+
:data:`~flask.request` variable is the request object associated with
20+
the current request, whereas :data:`~flask.g` is a general purpose
21+
variable associated with the current application context. We will go into
22+
the details of this a bit later.
23+
24+
For the time being all you have to know is that you can store information
25+
savely on the :data:`~flask.g` object.
26+
27+
So when do you put it on there? To do that you can make a helper
28+
function. The first time the function is called it will create a database
29+
connection for the current context and successive calls will return the
30+
already established connection::
31+
32+
def get_db():
33+
"""Opens a new database connection if there is none yet for the
34+
current application context.
35+
"""
36+
if not hasattr(g, 'sqlite_db'):
37+
g.sqlite_db = connect_db()
38+
return g.sqlite_db
39+
40+
41+
So now we know how to connect, but how do we properly disconnect? For
42+
that flask provides us with the :meth:`~flask.Flask.teardown_appcontext`
43+
decorator. It's executed every time the application context tears down::
44+
45+
@app.teardown_appcontext
46+
def close_db(error):
47+
"""Closes the database again at the end of the request."""
48+
if hasattr(g, 'sqlite_db'):
49+
g.sqlite_db.close()
50+
51+
Functions marked with :meth:`~flask.Flask.teardown_appcontext` are called
52+
every time the app context tears down. So what does this mean?
53+
Essentially the app context is created before the request comes in and is
54+
destroyed (teared down) whenever the request finishes. A teardown can
55+
happen because of two reasons: either everything went well (the error
56+
parameter will be `None`) or an exception happend in which case the error
57+
is passed to the teardown function.
58+
59+
Curious about what these contexts mean? Have a look at the
60+
:ref:`app-context` documentation to learn more.
61+
62+
Continue to :ref:`tutorial-dbinit`.
4763

4864
.. hint:: Where do I put this code?
4965

5066
If you've been following along in this tutorial, you might be wondering
5167
where to put the code from this step and the next. A logical place is to
5268
group these module-level functions together, and put your new
53-
``before_request`` and ``teardown_request`` functions below your existing
69+
``get_db`` and ``close_db`` functions below your existing
5470
``init_db`` function (following the tutorial line-by-line).
5571

5672
If you need a moment to find your bearings, take a look at how the `example

docs/tutorial/dbinit.rst

+26-23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.. _tutorial-dbinit:
22

3-
Step 3: Creating The Database
3+
Step 4: Creating The Database
44
=============================
55

66
Flaskr is a database powered application as outlined earlier, and more
@@ -20,36 +20,39 @@ to provide the path to the database there which leaves some place for
2020
errors. It's a good idea to add a function that initializes the database
2121
for you to the application.
2222

23-
If you want to do that, you first have to import the
24-
:func:`contextlib.closing` function from the contextlib package.
25-
Accordingly, add the following lines to your existing imports in `flaskr.py`::
26-
27-
from contextlib import closing
28-
29-
Next we can create a function called `init_db` that initializes the
30-
database. For this we can use the `connect_db` function we defined
31-
earlier. Just add that function below the `connect_db` function in
32-
`flaskr.py`::
23+
To do this we can create a function called `init_db` that initializes the
24+
database. Let me show you the code first. Just add that function below
25+
the `connect_db` function in `flaskr.py`::
3326

3427
def init_db():
35-
with closing(connect_db()) as db:
28+
app app.app_context():
29+
db = get_db()
3630
with app.open_resource('schema.sql', mode='r') as f:
3731
db.cursor().executescript(f.read())
3832
db.commit()
3933

40-
The :func:`~contextlib.closing` helper function allows us to keep a
41-
connection open for the duration of the `with` block. The
42-
:func:`~flask.Flask.open_resource` method of the application object
43-
supports that functionality out of the box, so it can be used in the
44-
`with` block directly. This function opens a file from the resource
34+
So what's happening here? Remember how we learned last chapter that the
35+
application context is created every time a request comes in? Here we
36+
don't have a request yet, so we need to create the application context by
37+
hand. Without an application context the :data:`~flask.g` object does not
38+
know yet to which application it becomes as there could be more than one!
39+
40+
The ``with app.app_context()`` statement establishes the application
41+
context for us. In the body of the with statement the :flask:`~flask.g`
42+
object will be associated with ``app``. At the end of the with statement
43+
the association is released and all teardown functions are executed. This
44+
means that our database connection is disconnected after the commit.
45+
46+
The :func:`~flask.Flask.open_resource` method of the application object
47+
is a convenient helper function that will open a resource that the
48+
application provides. This function opens a file from the resource
4549
location (your `flaskr` folder) and allows you to read from it. We are
4650
using this here to execute a script on the database connection.
4751

48-
When we connect to a database we get a connection object (here called
49-
`db`) that can give us a cursor. On that cursor there is a method to
50-
execute a complete script. Finally we only have to commit the changes.
51-
SQLite 3 and other transactional databases will not commit unless you
52-
explicitly tell it to.
52+
The connection object provided by SQLite can give us a cursor object.
53+
On that cursor there is a method to execute a complete script. Finally we
54+
only have to commit the changes. SQLite 3 and other transactional
55+
databases will not commit unless you explicitly tell it to.
5356

5457
Now it is possible to create a database by starting up a Python shell and
5558
importing and calling that function::
@@ -63,4 +66,4 @@ importing and calling that function::
6366
you did call the `init_db` function and that your table names are
6467
correct (singular vs. plural for example).
6568

66-
Continue with :ref:`tutorial-dbcon`
69+
Continue with :ref:`tutorial-views`

docs/tutorial/index.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ the `example source`_.
2424
folders
2525
schema
2626
setup
27-
dbinit
2827
dbcon
28+
dbinit
2929
views
3030
templates
3131
css

docs/tutorial/setup.rst

+41-29
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,18 @@ Step 2: Application Setup Code
55

66
Now that we have the schema in place we can create the application module.
77
Let's call it `flaskr.py` inside the `flaskr` folder. For starters we
8-
will add the imports we will need as well as the config section. For
9-
small applications it's a possibility to drop the configuration directly
10-
into the module which we will be doing here. However a cleaner solution
11-
would be to create a separate `.ini` or `.py` file and load that or import
12-
the values from there.
8+
will add the imports and create the application object. For small
9+
applications it's a possibility to drop the configuration directly into
10+
the module which we will be doing here. However a cleaner solution would
11+
be to create a separate `.ini` or `.py` file and load that or import the
12+
values from there.
1313

14-
In `flaskr.py`::
14+
First we add the imports in `flaskr.py`::
1515

1616
# all the imports
1717
import sqlite3
18-
from flask import Flask, request, session, g, redirect, url_for, \
19-
abort, render_template, flash
20-
21-
# configuration
22-
DATABASE = '/tmp/flaskr.db'
23-
DEBUG = True
24-
SECRET_KEY = 'development key'
25-
USERNAME = 'admin'
26-
PASSWORD = 'default'
27-
28-
.. admonition:: Windows
29-
30-
If you are on Windows, replace `/tmp/flaskr.db` with a different writeable
31-
path of your choice, in the configuration and for the rest of this
32-
tutorial.
18+
from flask import Flask, request, session, g, redirect, url_for, abort, \
19+
render_template, flash
3320

3421
Next we can create our actual application and initialize it with the
3522
config from the same file, in `flaskr.py`::
@@ -38,10 +25,24 @@ config from the same file, in `flaskr.py`::
3825
app = Flask(__name__)
3926
app.config.from_object(__name__)
4027

41-
:meth:`~flask.Config.from_object` will look at the given object (if it's a
42-
string it will import it) and then look for all uppercase variables
43-
defined there. In our case, the configuration we just wrote a few lines
44-
of code above. You can also move that into a separate file.
28+
# Load default config and override config from an environment variable
29+
app.config.update(dict(
30+
DATABASE='/tmp/flaskr.db',
31+
DEBUG=True,
32+
SECRET_KEY='development key',
33+
USERNAME='admin',
34+
PASSWORD='default'
35+
))
36+
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
37+
38+
The :class:`~flask.Config` object works similar to a dictionary so we
39+
can update it with new values.
40+
41+
.. admonition:: Windows
42+
43+
If you are on Windows, replace `/tmp/flaskr.db` with a different writeable
44+
path of your choice, in the configuration and for the rest of this
45+
tutorial.
4546

4647
Usually, it is a good idea to load a separate, environment specific
4748
configuration file. Flask allows you to import multiple configurations and it
@@ -54,20 +55,31 @@ Simply define the environment variable :envvar:`FLASKR_SETTINGS` that points to
5455
a config file to be loaded. The silent switch just tells Flask to not complain
5556
if no such environment key is set.
5657

57-
The `secret_key` is needed to keep the client-side sessions secure.
58+
In addition to that you can use the :meth:`~flask.Config.from_object`
59+
method on the config object and provide it with an import name of a
60+
module. Flask will the initialize the variable from that module. Note
61+
that in all cases only variable names that are uppercase are considered.
62+
63+
The ``SECRET_KEY`` is needed to keep the client-side sessions secure.
5864
Choose that key wisely and as hard to guess and complex as possible. The
5965
debug flag enables or disables the interactive debugger. *Never leave
6066
debug mode activated in a production system*, because it will allow users to
6167
execute code on the server!
6268

6369
We also add a method to easily connect to the database specified. That
6470
can be used to open a connection on request and also from the interactive
65-
Python shell or a script. This will come in handy later.
71+
Python shell or a script. This will come in handy later. We create a
72+
simple database connection through SQLite and then tell it to use the
73+
:class:`sqlite3.Row` object to represent rows. This allows us to treat
74+
the rows as if they were dictionaries instead of tuples.
6675

6776
::
6877

6978
def connect_db():
70-
return sqlite3.connect(app.config['DATABASE'])
79+
"""Connects to the specific database."""
80+
rv = sqlite3.connect(app.config['DATABASE'])
81+
rv.row_factory = sqlite3.Row
82+
return rv
7183

7284
Finally we just add a line to the bottom of the file that fires up the
7385
server if we want to run that file as a standalone application::
@@ -93,4 +105,4 @@ focus on that a little later. First we should get the database working.
93105
:ref:`externally visible server <public-server>` section for more
94106
information.
95107

96-
Continue with :ref:`tutorial-dbinit`.
108+
Continue with :ref:`tutorial-dbcon`.

docs/tutorial/views.rst

+8-8
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,17 @@ Show Entries
1212
This view shows all the entries stored in the database. It listens on the
1313
root of the application and will select title and text from the database.
1414
The one with the highest id (the newest entry) will be on top. The rows
15-
returned from the cursor are tuples with the columns ordered like specified
16-
in the select statement. This is good enough for small applications like
17-
here, but you might want to convert them into a dict. If you are
18-
interested in how to do that, check out the :ref:`easy-querying` example.
15+
returned from the cursor look a bit like tuples because we are using
16+
the :class:`sqlite3.Row` row factory.
1917

2018
The view function will pass the entries as dicts to the
2119
`show_entries.html` template and return the rendered one::
2220

2321
@app.route('/')
2422
def show_entries():
25-
cur = g.db.execute('select title, text from entries order by id desc')
26-
entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()]
23+
db = get_db()
24+
cur = db.execute('select title, text from entries order by id desc')
25+
entries = cur.fetchall()
2726
return render_template('show_entries.html', entries=entries)
2827

2928
Add New Entry
@@ -39,9 +38,10 @@ redirect back to the `show_entries` page::
3938
def add_entry():
4039
if not session.get('logged_in'):
4140
abort(401)
42-
g.db.execute('insert into entries (title, text) values (?, ?)',
41+
db = get_db()
42+
db.execute('insert into entries (title, text) values (?, ?)',
4343
[request.form['title'], request.form['text']])
44-
g.db.commit()
44+
db.commit()
4545
flash('New entry was successfully posted')
4646
return redirect(url_for('show_entries'))
4747

0 commit comments

Comments
 (0)