Using SQLite 3 with Flask

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

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

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

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

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

Example:

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

Note

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

Connect on Demand

The upside of this approach (connecting on first use) is that this willonly open the connection if truly necessary. If you want to use thiscode outside a request context you can use it in a Python shell by openingthe 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 thecurrent open database connection. To simplify working with SQLite, arow factory function is useful. It is executed for every result returnedfrom the database to convert the result. For instance, in order to getdictionaries instead of tuples, this could be inserted into the get_dbfunction 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.  
  5. 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 combinesgetting 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, makesworking with the database much more pleasant than it is by just using theraw 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 thestatement and pass in the arguments as a list. Never directly add them tothe SQL statement with string formatting because this makes it possibleto attack the application using SQL Injections.

Initial Schemas

Relational databases need schemas, so applications often ship aschema.sql file that creates the database. It’s a good idea to providea function that creates the database based on that schema. This functioncan 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()