Сообщить об ошибке.

Использование БД в приложении Flask на примере SQLite 3

Содержание:


Открытие и закрытие соединения с БД в приложении Flask.

В фреймворке 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() всегда выполняется, даже если предварительный обработчик запроса завершился неудачно или вообще никогда не выполнялся. Из-за этого необходимо убедиться, что соединение с базой данных существует, прежде чем его закрывать.

Ручное подключение контексту приложения Flask.

Положительная сторона этого подхода заключается в том, что соединение открывается только в том случае, если это действительно необходимо. Например, можно не тратить время и ресурсы сервера, что бы открыть и удерживать соединение в запросе к сайту, который не использует базу данных. Если нужно использовать этот код вне контекста запроса, то можно использовать его в интерпретаторе Python, открыв контекст приложения вручную:

with app.app_context():
    # теперь можно использовать get_db()

Работа с запросами к базе данных из приложения Flask.

Теперь в каждой функции-представлении можно получить доступ к функции 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-инъекций.

Создание пустой базы данных для приложения Flask.

Реляционным базам данных нужны схемы. Приложения часто имеют файл 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()