Использование SQLite 3 с Flask

Во Flask вы можете легко реализовать открытие соединений с базой данных по требованию и закрытие их, когда контекст умирает (обычно в конце запроса).

Вот простой пример того, как можно использовать SQLite 3 с Flask:

import sqlite3
from flask import g

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()

Теперь, чтобы использовать базу данных, приложение должно либо иметь активный контекст приложения (что всегда верно, если запрос находится в процессе выполнения), либо создать контекст приложения самостоятельно. В этот момент функция get_db может быть использована для получения текущего соединения с базой данных. При уничтожении контекста соединение с базой данных будет разорвано.

Пример:

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

Примечание

Пожалуйста, имейте в виду, что функции запроса на разрыв и appcontext выполняются всегда, даже если обработчик до запроса не сработал или не был выполнен. Из-за этого мы должны убедиться, что база данных существует, прежде чем мы ее закроем.

Подключение по требованию

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

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

Простота запросов

Теперь в каждой функции обработки запросов вы можете обратиться к get_db() для получения текущего открытого соединения с базой данных. Для упрощения работы с SQLite полезна функция row factory. Она выполняется для каждого результата, возвращаемого из базы данных, чтобы преобразовать результат. Например, чтобы получить словари вместо кортежей, в функцию get_db, которую мы создали выше, можно вставить следующее:

def make_dicts(cursor, row):
    return dict((cursor.description[idx][0], value)
                for idx, value in enumerate(row))

db.row_factory = make_dicts

Это заставит модуль sqlite3 возвращать dicts для этого соединения с базой данных, с которыми гораздо приятнее иметь дело. Еще проще, мы можем поместить это в get_db вместо:

db.row_factory = sqlite3.Row

В этом случае для возврата результатов запросов будут использоваться объекты Row, а не dicts. Это namedtuple s, поэтому мы можем обращаться к ним либо по индексу, либо по ключу. Например, предположим, что у нас есть sqlite3.Row под названием r для строк id, FirstName, LastName и MiddleInitial:

>>> # You can get values based on the row's name
>>> r['FirstName']
John
>>> # Or, you can get them based on index
>>> r[1]
John
# Row objects are also iterable:
>>> 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

Эта удобная маленькая функция в сочетании с фабрикой строк делает работу с базой данных намного приятнее, чем при использовании необработанных объектов курсора и соединения.

Вот как вы можете его использовать:

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 Injections.

Начальные схемы

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