В фреймворке Flask можно легко реализовать открытие соединения с базой данных в начале запроса URL-адреса сайта и закрытие соединения, когда контекст приложения выталкивается/умирает (обычно в конце запроса).
Простой пример того, как можно это реализовать на примере использования базы данных SQLite 3 в приложении Flask:
import sqlite3 from flask import g # расположение файла БД SQLite 3 DATABASE = '/path/to/database.db' def get_db(): """ Возвращает объект соединения с БД""" db = getattr(g, '_database', None) if db is None: db = g._database = sqlite3.connect(DATABASE) return db @app.teardown_appcontext def close_connection(exception): """Закрывает соединение с с БД""" db = getattr(g, '_database', None) if db is not None: db.close()
Теперь, чтобы использовать базу данных, приложение должно либо иметь активный контекст приложения (что всегда верно, если есть выполняемый запрос к URL сайта), либо создать контекст приложения вручную. В этот момент можно использовать функцию get_db()
для получения текущего соединения с базой данных. Всегда, когда контекст приложения разрушается/выталкивается, соединение с базой данных разрывается.
@app.route('/') def index(): cur = get_db().cursor() ...
Примечание: если используется Flask 0.9 или более ранняя версия, то нужно использовать flask._app_ctx_stack.top
вместо flask.g
, так как до Flask 1.0, объект flask.g
был привязан к контексту запроса, а не к контексту приложения.
Примечание. Имейте в виду, что декоратор экземпляра приложения app.teardown_appcontext()
всегда выполняется, даже если предварительный обработчик запроса завершился неудачно или вообще никогда не выполнялся. Из-за этого необходимо убедиться, что соединение с базой данных существует, прежде чем его закрывать.
Положительная сторона этого подхода заключается в том, что соединение открывается только в том случае, если это действительно необходимо. Например, можно не тратить время и ресурсы сервера, что бы открыть и удерживать соединение в запросе к сайту, который не использует базу данных. Если нужно использовать этот код вне контекста запроса, то можно использовать его в интерпретаторе Python, открыв контекст приложения вручную:
with app.app_context(): # теперь можно использовать get_db()
Теперь в каждой функции-представлении можно получить доступ к функции get_db()
, которая возвращает текущее открытое соединение с базой данных. Для упрощения работы с SQLite полезна высоко оптимизированная row_factory
. Эта фабрика поддерживает доступ к результату запроса как к словарю, где ключ это имя столбца. Она выполняется для каждого результата, возвращаемого из базы данных. Вставим код ниже в функцию get_db()
, которая была создана выше:
db.row_factory = sqlite3.Row
Это заставит модуль sqlite3
возвращать объекты Row
. Эти объекты представляют собой collections.namedtuple()
, поэтому, к ним можно получить доступ либо по индексу, либо по ключу. Например, предположим, что есть объект sqlite3.Row
, который называется r
с полями базы данных id
, FirstName
, LastName
, и MiddleInitial
:
# Можно получить значения по имени поля >>> r['FirstName'] # John # или по его индексу >>> r[1] # John # объекты `Row` можно перебирать в цикле: >>> for value in r: ... print(value) # 1 # John # Doe # M
Кроме того, рекомендуется создать функцию для запроса к БД, которая сочетает в себе получение курсора, выполнение запроса и получение результатов:
def query_db(query, args=(), one=False): cur = get_db().execute(query, args) rv = cur.fetchall() cur.close() return (rv[0] if rv else None) if one else rv
Эта небольшая функция в сочетании с фабрикой sqlite3.Row
делает работу с любой базой данных намного более приятной, чем при использовании простого курсора и объектов соединения.
Вот как это можно использовать:
for user in query_db('SELECT * FROM users'): print(user['username'], 'has the id', user['user_id'])
Или, если нужен только один результат:
user = query_db('SELECT * FROM users WHERE username = ?', [the_username], one=True) if user is None: print('No such user') else: print(the_username, 'has the id', user['user_id'])
Чтобы передать переменные части в SQL запрос, необходимо использовать вопросительный знак ?
и передавать параметры в виде списка. Никогда не добавляйте их напрямую в SQL запрос как форматированную строку, т.к. это позволяет атаковать приложение с помощью SQL-инъекций.
Реляционным базам данных нужны схемы. Приложения часто имеют файл schema.sql
, который создает пустую базу данных. Рекомендуется создать функцию, которая будет создавать базу данных на основе этой схемы.
Например:
def init_db(): with app.app_context(): db = get_db() with app.open_resource('schema.sql', mode='r') as f: db.cursor().executescript(f.read()) db.commit()
Затем можно создать такую базу данных из интерпретатора Python:
>>> from yourapplication import init_db >>> init_db()