Using SQLite 3 with Flask

In Flask you can easily implement the opening of database connections on demand and closing them when the context dies (usually at the end of the request).

Here is a simple example of how you can use SQLite 3 with Flask:

  1. import sqlite3
  2. from flask import g
  3. DATABASE = '/path/to/database.db'
  4. def get_db():
  5. db = getattr(g, '_database', None)
  6. if db is None:
  7. db = g._database = sqlite3.connect(DATABASE)
  8. return db
  9. @app.teardown_appcontext
  10. def close_connection(exception):
  11. db = getattr(g, '_database', None)
  12. if db is not None:
  13. db.close()

Now, to use the database, the application must either have an active application context (which is always true if there is a request in flight) or create an application context itself. At that point the get_db function can be used to get the current database connection. Whenever the context is destroyed the database connection will be terminated.

Note: if you use Flask 0.9 or older you need to use flask._app_ctx_stack.top instead of g as the flask.g object was bound to the request and not application context.

Example:

  1. @app.route('/')
  2. def index():
  3. cur = get_db().cursor()
  4. ...

Note

Please keep in mind that the teardown request and appcontext functions are always executed, even if a before-request handler failed or was never executed. Because of this we have to make sure here that the database is there before we close it.

Connect on Demand

The upside of this approach (connecting on first use) is that this will only open the connection if truly necessary. If you want to use this code outside a request context you can use it in a Python shell by opening the application context by hand:

  1. with app.app_context():
  2. # now you can use get_db()

Easy Querying

Now in each request handling function you can access get_db() to get the current open database connection. To simplify working with SQLite, a row factory function is useful. It is executed for every result returned from the database to convert the result. For instance, in order to get dictionaries instead of tuples, this could be inserted into the get_db function we created above:

  1. def make_dicts(cursor, row):
  2. return dict((cursor.description[idx][0], value)
  3. for idx, value in enumerate(row))
  4. db.row_factory = make_dicts

This will make the sqlite3 module return dicts for this database connection, which are much nicer to deal with. Even more simply, we could place this in get_db instead:

  1. db.row_factory = sqlite3.Row

This would use Row objects rather than dicts to return the results of queries. These are namedtuple s, so we can access them either by index or by key. For example, assuming we have a sqlite3.Row called r for the rows id, FirstName, LastName, and MiddleInitial:

  1. >>> # You can get values based on the row's name
  2. >>> r['FirstName']
  3. John
  4. >>> # Or, you can get them based on index
  5. >>> r[1]
  6. John
  7. # Row objects are also iterable:
  8. >>> for value in r:
  9. ... print(value)
  10. 1
  11. John
  12. Doe
  13. M

Additionally, it is a good idea to provide a query function that combines getting the cursor, executing and fetching the results:

  1. def query_db(query, args=(), one=False):
  2. cur = get_db().execute(query, args)
  3. rv = cur.fetchall()
  4. cur.close()
  5. return (rv[0] if rv else None) if one else rv

This handy little function, in combination with a row factory, makes working with the database much more pleasant than it is by just using the raw cursor and connection objects.

Here is how you can use it:

  1. for user in query_db('select * from users'):
  2. print(user['username'], 'has the id', user['user_id'])

Or if you just want a single result:

  1. user = query_db('select * from users where username = ?',
  2. [the_username], one=True)
  3. if user is None:
  4. print('No such user')
  5. else:
  6. print(the_username, 'has the id', user['user_id'])

To pass variable parts to the SQL statement, use a question mark in the statement and pass in the arguments as a list. Never directly add them to the SQL statement with string formatting because this makes it possible to attack the application using SQL Injections.

Initial Schemas

Relational databases need schemas, so applications often ship a schema.sql file that creates the database. It’s a good idea to provide a function that creates the database based on that schema. This function can do that for you:

  1. def init_db():
  2. with app.app_context():
  3. db = get_db()
  4. with app.open_resource('schema.sql', mode='r') as f:
  5. db.cursor().executescript(f.read())
  6. db.commit()

You can then create such a database from the Python shell:

  1. >>> from yourapplication import init_db
  2. >>> init_db()