• en
  • Language: ru
  • Documentation version: 1.1.x

Определение и доступ к базе данных

Приложение будет использовать базу данных SQLite для хранения пользователей и постов. Python поставляется со встроенной поддержкой SQLite в модуле sqlite3.

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

В учебнике нет подробного описания SQL. Если вы не знакомы с ним, в документации по SQLite описано language.

Подключение к базе данных

Первое, что необходимо сделать при работе с базой данных SQLite (и большинством других библиотек баз данных Python), - это создать соединение с ней. Любые запросы и операции выполняются с использованием этого соединения, которое закрывается после завершения работы.

В веб-приложениях это соединение обычно привязано к запросу. Оно создается в определенный момент при обработке запроса и закрывается перед отправкой ответа.

flaskr/db.py
import sqlite3

import click
from flask import current_app, g
from flask.cli import with_appcontext


def get_db():
    if 'db' not in g:
        g.db = sqlite3.connect(
            current_app.config['DATABASE'],
            detect_types=sqlite3.PARSE_DECLTYPES
        )
        g.db.row_factory = sqlite3.Row

    return g.db


def close_db(e=None):
    db = g.pop('db', None)

    if db is not None:
        db.close()

g - это специальный объект, уникальный для каждого запроса. Он используется для хранения данных, к которым могут обращаться несколько функций во время выполнения запроса. Соединение сохраняется и используется повторно вместо создания нового соединения, если get_db вызывается второй раз в том же запросе.

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

sqlite3.connect() устанавливает соединение с файлом, на который указывает ключ конфигурации DATABASE. Этот файл еще не должен существовать, и не будет существовать, пока вы не инициализируете базу данных позже.

sqlite3.Row указывает соединению возвращать строки, которые ведут себя как dicts. Это позволяет обращаться к столбцам по имени.

close_db проверяет, было ли создано соединение, проверяя, было ли установлено g.db. Если соединение существует, оно закрывается. Далее вы расскажете своему приложению о функции close_db в фабрике приложений, чтобы она вызывалась после каждого запроса.

Создание таблиц

В SQLite данные хранятся в таблицах и столбцах. Они должны быть созданы до того, как вы сможете хранить и извлекать данные. Flaskr будет хранить пользователей в таблице user, а посты в таблице post. Создайте файл с командами SQL, необходимыми для создания пустых таблиц:

flaskr/schema.sql
DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS post;

CREATE TABLE user (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  username TEXT UNIQUE NOT NULL,
  password TEXT NOT NULL
);

CREATE TABLE post (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  author_id INTEGER NOT NULL,
  created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  title TEXT NOT NULL,
  body TEXT NOT NULL,
  FOREIGN KEY (author_id) REFERENCES user (id)
);

Добавьте функции Python, которые будут выполнять эти SQL-команды, в файл db.py:

flaskr/db.py
def init_db():
    db = get_db()

    with current_app.open_resource('schema.sql') as f:
        db.executescript(f.read().decode('utf8'))


@click.command('init-db')
@with_appcontext
def init_db_command():
    """Clear the existing data and create new tables."""
    init_db()
    click.echo('Initialized the database.')

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

click.command() определяет команду командной строки init-db, которая вызывает функцию init_db и показывает пользователю сообщение об успехе. Вы можете прочитать Интерфейс командной строки, чтобы узнать больше о написании команд.

Зарегистрируйтесь в приложении

Функции close_db и init_db_command должны быть зарегистрированы в экземпляре приложения, иначе они не будут использоваться приложением. Однако, поскольку вы используете фабричную функцию, этот экземпляр недоступен при написании функций. Вместо этого напишите функцию, которая принимает приложение и выполняет регистрацию.

flaskr/db.py
def init_app(app):
    app.teardown_appcontext(close_db)
    app.cli.add_command(init_db_command)

app.teardown_appcontext() указывает Flask на вызов этой функции при очистке после возврата ответа.

app.cli.add_command() добавляет новую команду, которая может быть вызвана с помощью команды flask.

Импортируйте и вызовите эту функцию из фабрики. Поместите новый код в конец функции фабрики перед возвратом приложения.

flaskr/__init__.py
def create_app():
    app = ...
    # existing code omitted

    from . import db
    db.init_app(app)

    return app

Инициализация файла базы данных

Теперь, когда init-db зарегистрирован в приложении, его можно вызвать с помощью команды flask, аналогично команде run с предыдущей страницы.

Примечание

Если у вас все еще работает сервер с предыдущей страницы, вы можете либо остановить сервер, либо выполнить эту команду в новом терминале. Если вы используете новый терминал, не забудьте перейти в каталог вашего проекта и активировать env, как описано в Активируйте окружающую среду. Вам также нужно будет установить FLASK_APP и FLASK_ENV, как показано на предыдущей странице.

Выполните команду init-db:

$ flask init-db
Initialized the database.

Теперь в папке flaskr.sqlite в вашем проекте будет файл instance.

Продолжить Чертежи и виды.