3. Работа с базами данных в Flask
Цель лекции: Изучить основы работы фремворка Flask с распространными базами данных
Вопросы
- Введение в Flask и базы данных
Краткий обзор Flask как веб-фреймворка
Необходимость интеграции баз данных в веб-приложения
Обзор популярных баз данных для использования с Flask
- Работа с реляционными базами данных
SQLite для разработки и тестирования
PostgreSQL для промышленной эксплуатации
MySQL/MariaDB и их особенности Примеры подключения и конфигурации
- SQLAlchemy и Flask-SQLAlchemy
Основы ORM (Object-Relational Mapping)
Установка и настройка Flask-SQLAlchemy Определение моделей данных
CRUD-операции (Create, Read, Update, Delete)
Миграции базы данных с Alembic и Flask-Migrate
- NoSQL базы данных и Flask
MongoDB и PyMongo
Flask-PyMongo для упрощения интеграции
Работа с документоориентированной моделью данных
Примеры CRUD-операций в MongoDB
- Продвинутые техники работы с базами данных
Транзакции и обработка ошибок
Оптимизация запросов и индексирование
Пагинация и сортировка результатов
Полнотекстовый поиск
- Безопасность при работе с базами данных
SQL-инъекции и их предотвращение
Управление соединениями и пулами соединений
Шифрование конфиденциальных данных
Аудит и логирование операций с базой данных
- Redis и кэширование в Flask
Базовые концепции Redis
Flask-Caching для интеграции с Redis
Кэширование запросов и результатов
Очереди и фоновые задачи с Celery и Redis
1 Введение в Flask и базы данных
Напимним Flask — это легковесный веб-фреймворк для Python, позиционирующий себя как "микрофреймворк". Несмотря на свою компактность, Flask обеспечивает мощный и гибкий инструментарий для создания веб-приложений.
+---------------------+
| Flask |
+---------------------+
| - Маршрутизация |
| - Шаблонизатор |
| - Отладчик |
| - Сервер разработки|
+---------------------+
^
|
+---------------------+
| Расширения |
+---------------------+
| - Flask-SQLAlchemy |
| - Flask-Login |
| - Flask-WTF |
| - Flask-RESTful |
| - И другие... |
+---------------------+
Необходимость интеграции баз данных в веб-приложения
Современные веб-приложения редко обходятся без постоянного хранилища данных. Базы данных необходимы для:
Пользователь
^
|
+----------------+ +-----------------+
| Веб-приложение| <--> | База данных |
| (Flask) | | |
+----------------+ +-----------------+
^
|
Запрос/Ответ
- Хранения пользовательских данных: профили, предпочтения, настройки
- Сохранения состояния приложения: корзины покупок, сессии, временные данные
- Управления контентом: статьи, товары, комментарии
- Аналитики и отчетности: логи, статистика использования
- Обеспечения целостности данных: проверки, ограничения, связи между сущностями
Обзор популярных баз данных для использования с Flask
Реляционные базы данных
+-------------------+ +-------------------+
| SQLite | | PostgreSQL |
+-------------------+ +-------------------+
| - Файловая БД | | - Производитель- |
| - Встроенная | | ность |
| - Без сервера | | - Расширяемость |
| - Для разработки | | - Для production |
+-------------------+ +-------------------+
+-------------------+ +-------------------+
| MySQL/MariaDB | | Oracle |
+-------------------+ +-------------------+
| - Популярность | | - Корпоративный |
| - Простота | | стандарт |
| - Для web-apps | | - Высокая |
| - Оптимизирован | | надежность |
+-------------------+ +-------------------+
NoSQL базы данных
+-------------------+ +-------------------+
| MongoDB | | Redis |
+-------------------+ +-------------------+
| - Документо- | | - Хранилище |
| ориентированная | | ключ-значение |
| - JSON-подобные | | - Кэширование |
| документы | | - Быстродействие |
| - Гибкие схемы | | - In-memory |
+-------------------+ +-------------------+
+-------------------+ +-------------------+
| Cassandra | | Firebase |
+-------------------+ +-------------------+
| - Распределенная | | - BaaS решение |
| - Высокая | | - Realtime DB |
| доступность | | - Облачное |
| - Масштабируемость| | хранилище |
+-------------------+ +-------------------+
Сравнение подходов при интеграции с Flask
+---------------------------------------------+
| Интеграция с Flask |
+---------------------------------------------+
| |
| +----------------+ +----------------+ |
| | Нативные драйверы| | ORM | |
| +----------------+ +----------------+ |
| | - psycopg2 | | - SQLAlchemy | |
| | - pymysql | | - Peewee | |
| | - pymongo | | - Django ORM | |
| +----------------+ +----------------+ |
| |
| +----------------+ +----------------+ |
| | Flask-расширения| | API-клиенты | |
| +----------------+ +----------------+ |
| | - Flask-SQLAlchemy| | - Redis-py | |
| | - Flask-PyMongo | | - Elasticsearch | |
| | - Flask-Redis | | - Neo4j-python | |
| +----------------+ +----------------+ |
| |
+---------------------------------------------+
Выбор базы данных зависит от: - Требований к производительности и масштабируемости - Структуры хранимых данных - Особенностей предметной области - Опыта разработчиков - Требований к целостности и согласованности данных
2. Работа с реляционными базами данных
SQLite для разработки и тестирования
SQLite — это легковесная встраиваемая реляционная база данных, которая хранит все данные в одном файле на диске.
+----------------------------+
| SQLite |
+----------------------------+
| |
| Файл базы данных |
| +------------------+ |
| | Таблицы | |
| | Индексы | |
| | Триггеры | |
| | Представления | |
| +------------------+ |
| |
+----------------------------+
Почему SQLite удобна для разработки: - Не требует установки отдельного сервера - Вся база данных — это один файл - Встроена в Python (не нужны дополнительные библиотеки) - Поддерживает большинство стандартных SQL-запросов - Быстрая для небольших приложений
Пример работы с SQLite в Flask:
import sqlite3
from flask import Flask, g
app = Flask(__name__)
DATABASE = 'database.db' # Путь к файлу базы данных
def get_db():
# Проверяем, есть ли уже соединение с БД в контексте запроса
db = getattr(g, '_database', None)
if db is None:
# Если соединения нет, создаем новое
db = g._database = sqlite3.connect(DATABASE)
db.row_factory = sqlite3.Row # Результаты в виде словарей, а не кортежей
return db
@app.teardown_appcontext
def close_connection(exception):
# Эта функция автоматически вызывается в конце запроса
# и закрывает соединение с БД
db = getattr(g, '_database', None)
if db is not None:
db.close()
@app.route('/users')
def get_users():
db = get_db() # Получаем соединение с БД
cursor = db.cursor() # Создаем курсор для выполнения SQL-запросов
cursor.execute('SELECT id, name, email FROM users') # Выполняем запрос
users = cursor.fetchall() # Получаем все результаты
return {'users': [dict(user) for user in users]} # Преобразуем в словари и возвращаем
В ручке /users о нет явного вызова функции close_connection, и это ключевой момент для понимания работы декоратора @app.teardown_appcontext.
Когда вы используете декоратор @app.teardown_appcontext, Flask автоматически вызывает декорированную функцию в конце жизненного цикла контекста приложения, который создается для каждого HTTP-запроса. Это происходит следующим образом:
-
Когда поступает HTTP-запрос к эндпоинту
/users, Flask создает контекст приложения. -
Функция
get_users()выполняется и вызываетget_db()для получения соединения с базой данных, которое сохраняется в объектеg. -
После того как функция
get_users()завершила работу и сформировала ответ, Flask начинает процесс очистки контекста приложения. -
В этот момент Flask автоматически вызывает все функции, зарегистрированные через декоратор
@app.teardown_appcontext. -
Наша функция
close_connection()выполняется, проверяет наличие соединения с БД и закрывает его, если оно существует.
Важно понимать, что этот механизм работает независимо от того, завершился ли запрос успешно или с ошибкой. Даже если в get_users() произойдет исключение, Flask все равно вызовет close_connection(), что гарантирует закрытие соединения с базой данных.
Flask берет на себя управление жизненным циклом ресурсов, избавляя разработчика от необходимости вручную контролировать открытие и закрытие соединений в каждом обработчике маршрута.
Курсор базы данных — это объект уровня базы данных, который позволяет запрашивать базу данных несколько раз.
В контексте Flask объект g - это специальный объект глобального контекста запроса. "g" означает "global", но является глобальным только в рамках одного запроса. Это временное хранилище, доступное во время обработки одного конкретного HTTP-запроса.
Ключевые особенности объекта g:
-
Область видимости запроса: Данные, сохраненные в
g, доступны только во время обработки текущего запроса и автоматически очищаются после его завершения. -
Удобство для хранения соединений с БД: Как показано в нашем примере кода,
gидеально подходит для хранения соединения с базой данных, чтобы не создавать его повторно при каждом обращении внутри одного запроса. -
Безопасность в многопоточной среде: Каждый запрос имеет свой собственный объект
g, что делает его безопасным для использования в многопоточных приложениях. -
Доступность во всех функциях представления: Объект
gдоступен в любой функции, обрабатывающей запрос, без необходимости передавать его как параметр.
PostgreSQL для промышленной эксплуатации
PostgreSQL — это объектно-реляционная система управления базами данных с открытым исходным кодом.
+--------------------------------------+
| PostgreSQL |
+--------------------------------------+
| |
| +------------------------------+ |
| | PostgreSQL сервер | |
| | +------------------------+ | |
| | | База данных | | |
| | | +----------------+ | | |
| | | | Схемы | | | |
| | | | +----------+ | | | |
| | | | | Таблицы | | | | |
| | | | +----------+ | | | |
| | | +----------------+ | | |
| | +------------------------+ | |
| +------------------------------+ |
| |
+--------------------------------------+
Преимущества PostgreSQL: - Высокая производительность - Надежность и стабильность - Отличная поддержка транзакций - Расширяемость (функции, типы данных) - Продвинутые индексы и оптимизация запросов - Работа с JSON, геоданными и другими сложными типами
Пример работы с PostgreSQL в Flask:
import psycopg2
import psycopg2.extras
from flask import Flask, g
app = Flask(__name__)
DATABASE_URI = "postgresql://username:password@localhost/dbname" # Строка подключения
def get_db():
# Проверяем, есть ли уже соединение с БД в контексте запроса
db = getattr(g, '_database', None)
if db is None:
# Если соединения нет, создаем новое
db = g._database = psycopg2.connect(DATABASE_URI)
return db
@app.teardown_appcontext
def close_connection(exception):
# Автоматически закрываем соединение в конце запроса
db = getattr(g, '_database', None)
if db is not None:
db.close()
@app.route('/products')
def get_products():
db = get_db() # Получаем соединение
# Создаем курсор, который будет возвращать результаты в виде словарей
cursor = db.cursor(cursor_factory=psycopg2.extras.DictCursor)
cursor.execute('SELECT id, name, price FROM products') # Выполняем запрос
products = cursor.fetchall() # Получаем все результаты
return {'products': [dict(product) for product in products]} # Возвращаем как JSON
MySQL/MariaDB и их особенности
MySQL — популярная система управления базами данных с открытым исходным кодом. MariaDB — это ответвление (форк) MySQL, созданное сообществом.
+--------------------------------------+
| MySQL/MariaDB |
+--------------------------------------+
| |
| +------------------------------+ |
| | MySQL сервер | |
| | +------------------------+ | |
| | | База данных | | |
| | | +----------------+ | | |
| | | | Таблицы | | | |
| | | | (InnoDB, | | | |
| | | | MyISAM и др.)| | | |
| | | +----------------+ | | |
| | +------------------------+ | |
| +------------------------------+ |
| |
+--------------------------------------+
Особенности MySQL/MariaDB: - Высокая скорость для операций чтения - Легкая настройка и администрирование - Различные движки хранения данных (InnoDB, MyISAM) - Отличная документация и большое сообщество - Оптимизирована для веб-приложений
Пример работы с MySQL в Flask:
import pymysql
from flask import Flask, g
app = Flask(__name__)
DATABASE_CONFIG = {
'host': 'localhost', # Хост базы данных
'user': 'username', # Имя пользователя
'password': 'password', # Пароль
'db': 'dbname', # Имя базы данных
'charset': 'utf8mb4', # Кодировка (поддержка эмодзи и других Unicode символов)
'cursorclass': pymysql.cursors.DictCursor # Тип курсора (результаты в виде словарей)
}
def get_db():
# Получаем существующее соединение или создаем новое
db = getattr(g, '_database', None)
if db is None:
db = g._database = pymysql.connect(**DATABASE_CONFIG)
return db
@app.teardown_appcontext
def close_connection(exception):
# Закрываем соединение после завершения запроса
db = getattr(g, '_database', None)
if db is not None:
db.close()
@app.route('/orders')
def get_orders():
db = get_db()
with db.cursor() as cursor: # Используем менеджер контекста для автоматического закрытия курсора
cursor.execute('SELECT id, customer_id, total_price, status FROM orders')
orders = cursor.fetchall() # Получаем все заказы
return {'orders': orders} # Возвращаем как JSON
Примеры
SQLite:
def init_sqlite_db():
# Подключаемся к файлу базы данных
conn = sqlite3.connect('database.db')
c = conn.cursor()
# Создаем таблицу users, если она не существует
c.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT, # Автоинкрементный первичный ключ
username TEXT NOT NULL, # Имя пользователя (не может быть NULL)
email TEXT UNIQUE NOT NULL, # Уникальный email
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP # Время создания
)
''')
conn.commit() # Сохраняем изменения
conn.close() # Закрываем соединение
PostgreSQL:
def init_postgres_db():
# Подключаемся к серверу PostgreSQL
conn = psycopg2.connect("postgresql://username:password@localhost/dbname")
conn.autocommit = True # Автоматический коммит изменений
cursor = conn.cursor()
# Создаем таблицу users, если она не существует
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY, # Автоинкрементный первичный ключ
username VARCHAR(100) NOT NULL, # Имя пользователя (строка фикс. длины)
email VARCHAR(100) UNIQUE NOT NULL, # Уникальный email
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP # Время создания
)
''')
cursor.close() # Закрываем курсор
conn.close() # Закрываем соединение
MySQL:
def init_mysql_db():
# Подключаемся к серверу MySQL
conn = pymysql.connect(
host='localhost',
user='username',
password='password',
db='dbname'
)
cursor = conn.cursor()
# Создаем таблицу users, если она не существует
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY, # Автоинкрементный первичный ключ
username VARCHAR(100) NOT NULL, # Имя пользователя
email VARCHAR(100) UNIQUE NOT NULL, # Уникальный email
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP # Время создания
)
''')
conn.commit() # Сохраняем изменения
cursor.close() # Закрываем курсор
conn.close() # Закрываем соединение
Сравнение баз данных в виде таблицы
+------------------+----------------+------------------+------------------+
| Функция | SQLite | PostgreSQL | MySQL/MariaDB |
+------------------+----------------+------------------+------------------+
| Тип базы данных | Файловая | Клиент-серверная | Клиент-серверная |
+------------------+----------------+------------------+------------------+
| Установка | Не требуется | Требуется | Требуется |
+------------------+----------------+------------------+------------------+
| Конфигурация | Минимальная | Сложная | Средняя |
+------------------+----------------+------------------+------------------+
| Производитель- | Низкая для | Высокая | Средняя/Высокая |
| ность | больших данных | | |
+------------------+----------------+------------------+------------------+
| Параллельное | Ограниченное | Отличное | Хорошее |
| выполнение | | | |
+------------------+----------------+------------------+------------------+
| Сложные запросы | Ограниченно | Полная поддержка | Хорошая поддержка|
+------------------+----------------+------------------+------------------+
| Типичное | Разработка, | Промышленная | Веб-приложения, |
| применение | тестирование, | эксплуатация, | CMS, блоги |
| | мобильные | корпоративные | |
| | приложения | приложения | |
+------------------+----------------+------------------+------------------+
Выбор базы данных для приложений Flask зависит от конкретных требований и условий:
- SQLite идеально подходит для:
- Разработки и тестирования
- Небольших приложений
- Встраиваемых решений
-
Ситуаций, когда простота важнее масштабируемости
-
PostgreSQL рекомендуется для:
- Корпоративных приложений
- Систем с высокими требованиями к надежности
- Приложений со сложной бизнес-логикой
-
Использования расширенных типов данных (JSON, геоданные)
-
MySQL/MariaDB хорошо подходит для:
- Веб-сайтов с высокой посещаемостью
- Приложений с преобладанием операций чтения
- Систем управления контентом
- Если у вашей команды есть опыт работы с MySQL
3. SQLAlchemy и Flask-SQLAlchemy
SQLAlchemy — это ORM (Object-Relational Mapping, объектно-реляционное отображение). Это библиотека для работы с базами данных в Python, которая позволяет взаимодействовать с реляционными базами данных через объекты Python, а не напрямую через SQL-запросы.
ORM — это подход программирования, который позволяет представлять таблицы базы данных в виде классов и объектов. Вместо того чтобы писать сложные SQL-запросы, вы работаете с объектами Python, которые автоматически преобразуются в соответствующие SQL-операции.
Пример:
- Таблица в базе данных становится классом Python.
- Строка в таблице становится экземпляром этого класса.
- Столбцы таблицы становятся атрибутами класса.
SQLAlchemy состоит из двух основных компонентов:
-
Core : Низкоуровневый API, который предоставляет инструменты для работы с базами данных через SQL-выражения. Позволяет выполнять "сырые" SQL-запросы или строить запросы с помощью абстрактного синтаксиса Python. Подходит для разработчиков, которым нужен контроль над SQL.
-
ORM : Высокоуровневый API, который предоставляет объектно-ориентированный интерфейс для работы с базой данных. Позволяет работать с таблицами как с классами и строками как с объектами. Упрощает работу с базой данных за счет автоматического преобразования операций с объектами в SQL-запросы.
+----------------------------------+
| Ваше приложение |
| |
| +----------------------------+ |
| | Python-код | |
| +----------------------------+ |
| ↓ |
| +----------------------------+ |
| | SQLAlchemy | |
| +----------------------------+ |
| ↓ |
+----------------------------------+
↓
+----------------------------------+
| База данных |
+----------------------------------+
Представьте, что SQLAlchemy — это переводчик: - Вы говорите на Python - База данных говорит на SQL - SQLAlchemy переводит ваши Python-команды в SQL-запросы
Зачем нужен Flask-SQLAlchemy?
Flask-SQLAlchemy — это расширение для Flask, которое упрощает использование SQLAlchemy в проектах на Flask.
+----------------------------------+
| Flask-приложение |
| +----------------------------+ |
| | Веб-страницы | |
| +----------------------------+ |
| ↓ |
| +----------------------------+ |
| | Flask-SQLAlchemy | |
| +----------------------------+ |
| ↓ |
| +----------------------------+ |
| | SQLAlchemy | |
| +----------------------------+ |
| ↓ |
+----------------------------------+
↓
+----------------------------------+
| База данных |
+----------------------------------+
Что Flask-SQLAlchemy делает проще: - Автоматически настраивает соединение с базой данных - Управляет сессиями - Интегрируется с системой запросов Flask - Предоставляет полезные вспомогательные функции
Установка и настройка
Установить Flask-SQLAlchemy очень просто:
pip install Flask-SQLAlchemy
Настройка в вашем приложении:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
# Создаем объект приложения
app = Flask(__name__)
# Говорим приложению, где находится база данных
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///students.db'
# Отключаем отслеживание изменений (для производительности)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# Создаем объект для работы с базой данных
db = SQLAlchemy(app)
Модели данных — это описание таблиц вашей базы данных на языке Python. Каждая модель — это класс Python, который соответствует таблице в базе данных.
+----------------------------------+
| |
| Python-класс Student |
| +----------------------------+ |
| | id | |
| | name | |
| | age | |
| | email | |
| +----------------------------+ |
| ↓ |
+----------------------------------+
↓
+----------------------------------+
| |
| Таблица students в базе данных |
| +----------------------------+ |
| | id | name | age | email | |
| |----+------+-----+----------| |
| | 1 | Анна | 20 | a@mail.ru| |
| | 2 | Иван | 21 | i@mail.ru| |
| +----------------------------+ |
| |
+----------------------------------+
Пример модели студента:
class Student(db.Model):
# Название таблицы (необязательно, по умолчанию - имя класса в нижнем регистре)
__tablename__ = 'students'
# Колонки таблицы
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
age = db.Column(db.Integer)
email = db.Column(db.String(120), unique=True)
# Метод для отображения объекта в виде строки
def __repr__(self):
return f'<Student {self.name}>'
Основные типы данных в SQLAlchemy:
+-------------------+----------------------------+
| Тип в SQLAlchemy | Для чего используется |
+-------------------+----------------------------+
| Integer | Целые числа |
| String(длина) | Текст ограниченной длины |
| Text | Текст без ограничений |
| Float | Числа с плавающей точкой |
| Boolean | Логические значения |
| DateTime | Дата и время |
+-------------------+----------------------------+
Связи между таблицами
В реальных приложениях таблицы связаны между собой. Например, у студента может быть несколько курсов.
+----------------------------------+ +----------------------------------+
| | | |
| Модель Student | | Модель Course |
| +----------------------------+ | | +----------------------------+ |
| | id | | | | id | |
| | name | | | | title | |
| | courses (связь) |◆-------→| | students (связь) | |
| +----------------------------+ | | +----------------------------+ |
| | | |
+----------------------------------+ +----------------------------------+
Пример связи "многие-ко-многим":
# Таблица для связи студентов и курсов
student_courses = db.Table('student_courses',
db.Column('student_id', db.Integer, db.ForeignKey('students.id')),
db.Column('course_id', db.Integer, db.ForeignKey('courses.id'))
)
class Student(db.Model):
__tablename__ = 'students'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
# Связь с курсами
courses = db.relationship('Course', secondary=student_courses,
backref=db.backref('students', lazy='dynamic'))
class Course(db.Model):
__tablename__ = 'courses'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
Основные операции с данными (CRUD)
CRUD — это аббревиатура от Create (создание), Read (чтение), Update (обновление) и Delete (удаление).
+----------------------------------+
| CRUD-операции |
+----------------------------------+
| |
| Create → Новая запись |
| |
| Read → Получение данных |
| |
| Update → Изменение данных |
| |
| Delete → Удаление данных |
| |
+----------------------------------+
1. Создание записей (Create)
# Создаем нового студента
new_student = Student(name='Мария', age=19, email='maria@example.com')
# Добавляем его в сессию базы данных
db.session.add(new_student)
# Сохраняем изменения в базе данных
db.session.commit()
2. Чтение данных (Read)
# Получить всех студентов
all_students = Student.query.all()
# Найти студента по ID
student = Student.query.get(1)
# Найти студента по имени
maria = Student.query.filter_by(name='Мария').first()
# Сложный запрос: найти всех студентов старше 18 лет, отсортированных по имени
adult_students = Student.query.filter(Student.age > 18).order_by(Student.name).all()
3. Обновление данных (Update)
# Найти студента, которого хотим обновить
student = Student.query.get(1)
# Изменить его данные
student.age = 21
student.email = 'new_email@example.com'
# Сохранить изменения
db.session.commit()
4. Удаление данных (Delete)
# Найти студента, которого хотим удалить
student_to_delete = Student.query.get(2)
# Удалить его
db.session.delete(student_to_delete)
# Сохранить изменения
db.session.commit()
Миграции базы данных
Со временем структура вашей базы данных будет меняться: добавляются новые таблицы, изменяются существующие. Flask-Migrate помогает управлять этими изменениями.
+----------------------------------+
| Процесс миграции базы данных |
| |
| 1. Изменение моделей Python →|
| ↓ |
| 2. Создание файла миграции →|
| ↓ |
| 3. Применение миграции к БД →|
| ↓ |
| 4. Обновленная база данных |
| |
+----------------------------------+
Установка Flask-Migrate:
pip install Flask-Migrate
Настройка:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
db = SQLAlchemy(app)
migrate = Migrate(app, db)
# Ваши модели...
Основные команды для работы с миграциями:
# Инициализация системы миграций
flask db init
# Создание миграции после изменения моделей
flask db migrate -m "Добавлена таблица студентов"
# Применение миграции к базе данных
flask db upgrade
# Откат последней миграции (если что-то пошло не так)
flask db downgrade
Пример использования в Flask-приложении
Вот как все это выглядит в полноценном Flask-приложении:
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///students.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
# Определение модели
class Student(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
age = db.Column(db.Integer)
def __repr__(self):
return f'<Student {self.name}>'
# Создание таблиц в базе данных
with app.app_context():
db.create_all()
# Маршруты приложения
@app.route('/')
def index():
students = Student.query.all()
return render_template('index.html', students=students)
@app.route('/add', methods=['GET', 'POST'])
def add_student():
if request.method == 'POST':
name = request.form['name']
age = request.form['age']
new_student = Student(name=name, age=age)
db.session.add(new_student)
db.session.commit()
return redirect(url_for('index'))
return render_template('add.html')
if __name__ == '__main__':
app.run(debug=True)
4. NoSQL базы данных и Flask
MongoDB — это популярная NoSQL база данных, которая хранит данные в формате JSON-подобных документов. В отличие от реляционных баз данных, MongoDB не требует строгой схемы для структуры данных.
+--------------------------------------+
| MongoDB |
+--------------------------------------+
| |
| +------------------------------+ |
| | База данных | |
| | +------------------------+ | |
| | | Коллекции | | |
| | | +------------------+ | | |
| | | | Документы | | | |
| | | | (JSON-подобные) | | | |
| | | +------------------+ | | |
| | +------------------------+ | |
| +------------------------------+ |
| |
+--------------------------------------+
Отличия MongoDB от реляционных баз данных: - Хранит документы вместо строк таблиц - Не требует фиксированной схемы - Естественная поддержка вложенных данных - Работает с BSON (бинарный JSON) - Горизонтальное масштабирование
Установка PyMongo:
# Установка библиотеки для работы с MongoDB
pip install pymongo
Простой пример подключения к MongoDB:
from pymongo import MongoClient
from flask import Flask, jsonify
app = Flask(__name__)
# Создаем соединение с MongoDB
client = MongoClient('mongodb://localhost:27017/') # Стандартный адрес для локального сервера MongoDB
# Выбираем базу данных
db = client['flask_app'] # Имя базы данных
# Получаем коллекцию (аналог таблицы в реляционных БД)
users_collection = db['users'] # Коллекция пользователей
# Пример маршрута для получения всех пользователей
@app.route('/users')
def get_users():
# Получаем всех пользователей из коллекции
users = list(users_collection.find({}, {'_id': 0})) # Исключаем поле _id из результатов
return jsonify(users)
Flask-PyMongo для упрощения интеграции
Flask-PyMongo — это расширение Flask, которое упрощает работу с MongoDB.
+--------------------------------------+
| Flask-приложение |
+--------------------------------------+
| |
| +------------------------------+ |
| | Flask-PyMongo | |
| +------------------------------+ |
| ↓ |
| +------------------------------+ |
| | PyMongo | |
| +------------------------------+ |
| ↓ |
+--------------------------------------+
↓
+--------------------------------------+
| MongoDB сервер |
+--------------------------------------+
Установка Flask-PyMongo:
pip install Flask-PyMongo
Настройка Flask-PyMongo:
from flask import Flask, jsonify, request
from flask_pymongo import PyMongo
app = Flask(__name__)
# Настройка подключения к MongoDB
app.config['MONGO_URI'] = 'mongodb://localhost:27017/flask_app'
mongo = PyMongo(app) # Создаем экземпляр PyMongo
@app.route('/users')
def get_users():
# Получаем всех пользователей (mongo.db автоматически указывает на нашу базу данных)
users = list(mongo.db.users.find({}, {'_id': 0}))
return jsonify(users)
Работа с документоориентированной моделью данных
В MongoDB данные хранятся в виде документов — гибких структур, похожих на JSON-объекты. Это позволяет хранить разнородные данные в одной коллекции.
+----------------------------------------+
| Документ пользователя в MongoDB |
+----------------------------------------+
| |
| { |
| "_id": ObjectId("5f8a53d3e8d1d"), |
| "username": "john_doe", |
| "email": "john@example.com", |
| "profile": { |
| "first_name": "John", |
| "last_name": "Doe", |
| "age": 30 |
| }, |
| "interests": ["coding", "music"], |
| "joined_at": ISODate("2023-05-15") |
| } |
| |
+----------------------------------------+
Преимущества документной модели: - Естественное представление объектов - Гибкость схемы (разные документы могут иметь разную структуру) - Возможность хранить вложенные структуры - Хорошая производительность для операций с целыми документами
Примеры CRUD-операций в MongoDB
1. Создание (Create)
@app.route('/users', methods=['POST'])
def create_user():
# Получаем данные из запроса
user_data = request.json
# Проверяем, что пользователь с таким email не существует
if mongo.db.users.find_one({'email': user_data['email']}):
return jsonify({'error': 'Пользователь с таким email уже существует'}), 400
# Добавляем пользователя в коллекцию
result = mongo.db.users.insert_one(user_data)
# Возвращаем ID созданного пользователя
return jsonify({'message': 'Пользователь создан', 'id': str(result.inserted_id)}), 201
2. Чтение (Read)
# Получение всех пользователей
@app.route('/users', methods=['GET'])
def get_all_users():
# Получаем параметры запроса (например, для фильтрации)
age = request.args.get('age', type=int)
# Формируем фильтр
query = {}
if age:
query['profile.age'] = age # Обращение к вложенному полю с помощью точки
# Выполняем запрос с фильтром
users = list(mongo.db.users.find(query, {'_id': 0}))
return jsonify(users)
# Получение пользователя по имени пользователя
@app.route('/users/<username>', methods=['GET'])
def get_user(username):
# Ищем пользователя по имени пользователя
user = mongo.db.users.find_one({'username': username}, {'_id': 0})
if user:
return jsonify(user)
else:
return jsonify({'error': 'Пользователь не найден'}), 404
3. Обновление (Update)
@app.route('/users/<username>', methods=['PUT'])
def update_user(username):
# Получаем данные для обновления
update_data = request.json
# Обновляем пользователя
result = mongo.db.users.update_one(
{'username': username}, # Фильтр - какой документ обновлять
{'$set': update_data} # Оператор $set устанавливает новые значения полей
)
if result.matched_count > 0:
return jsonify({'message': 'Пользователь обновлен'})
else:
return jsonify({'error': 'Пользователь не найден'}), 404
4. Удаление (Delete)
@app.route('/users/<username>', methods=['DELETE'])
def delete_user(username):
# Удаляем пользователя
result = mongo.db.users.delete_one({'username': username})
if result.deleted_count > 0:
return jsonify({'message': 'Пользователь удален'})
else:
return jsonify({'error': 'Пользователь не найден'}), 404
Специальные операторы MongoDB
MongoDB предоставляет множество операторов для выполнения сложных запросов:
# Поиск пользователей старше 25 лет
users = mongo.db.users.find({'profile.age': {'$gt': 25}})
# Поиск пользователей с определенными интересами
users = mongo.db.users.find({'interests': {'$in': ['coding', 'music']}})
# Обновление с увеличением значения
result = mongo.db.users.update_one(
{'username': 'john_doe'},
{'$inc': {'visits': 1}} # Увеличиваем счетчик посещений на 1
)
# Добавление элемента в массив
result = mongo.db.users.update_one(
{'username': 'john_doe'},
{'$push': {'interests': 'photography'}} # Добавляем новый интерес
)
Полный пример Flask-приложения с MongoDB
from flask import Flask, jsonify, request
from flask_pymongo import PyMongo
from bson.objectid import ObjectId
import datetime
app = Flask(__name__)
app.config['MONGO_URI'] = 'mongodb://localhost:27017/task_manager'
mongo = PyMongo(app)
# Маршрут для создания новой задачи
@app.route('/tasks', methods=['POST'])
def create_task():
# Получаем данные из запроса
title = request.json.get('title')
description = request.json.get('description', '')
due_date = request.json.get('due_date')
# Проверяем обязательные поля
if not title:
return jsonify({'error': 'Название задачи обязательно'}), 400
# Создаем новый документ задачи
new_task = {
'title': title,
'description': description,
'completed': False,
'created_at': datetime.datetime.now(),
'due_date': datetime.datetime.fromisoformat(due_date) if due_date else None
}
# Вставляем задачу в коллекцию tasks
result = mongo.db.tasks.insert_one(new_task)
# Возвращаем ID созданной задачи
return jsonify({
'message': 'Задача создана',
'id': str(result.inserted_id)
}), 201
# Маршрут для получения всех задач
@app.route('/tasks', methods=['GET'])
def get_tasks():
# Получаем параметры фильтрации
completed = request.args.get('completed')
# Строим запрос
query = {}
if completed is not None:
query['completed'] = completed.lower() == 'true'
# Получаем задачи с фильтром
tasks = []
for task in mongo.db.tasks.find(query):
# Преобразуем ObjectId в строку для JSON
task['_id'] = str(task['_id'])
# Преобразуем даты в ISO формат
if 'created_at' in task:
task['created_at'] = task['created_at'].isoformat()
if 'due_date' in task and task['due_date']:
task['due_date'] = task['due_date'].isoformat()
tasks.append(task)
return jsonify({'tasks': tasks})
if __name__ == '__main__':
app.run(debug=True)
Заключение
Сегодня мы рассмотрели комплексный подход к интеграции баз данных в веб-приложения с использованием фреймворка Flask. Позвольте подытожить ключевые моменты нашей лекции.
Flask, как легковесный и гибкий веб-фреймворк, предоставляет нам основу для разработки современных веб-приложений. Однако его истинная сила раскрывается при интеграции с системами хранения данных.
В контексте реляционных БД мы детально изучили SQLite – решение для быстрой разработки и тестирования, что было продемонстрировано в нашем практическом примере с использованием контекстного менеджера g. Мы также обсудили PostgreSQL как предпочтительный выбор для эксплуатации благодаря его надежности и поддержке сложных типов данных. MySQL/MariaDB представляют собой компромиссное решение с хорошими показателями производительности.
Особое внимание мы уделили SQLAlchemy – ORM-системе, которая абстрагирует работу с базой данных, позволяя оперировать объектами Python вместо написания SQL-запросов напрямую. Flask-SQLAlchemy значительно упрощает интеграцию, предоставляя элегантный API для определения моделей и выполнения CRUD-операций. Система миграций Alembic и Flask-Migrate позволяет контролировать эволюцию схемы данных, что критически важно при промышленной разработке.
В современной экосистеме разработки нельзя игнорировать NoSQL решения. Мы детально разобрали интеграцию MongoDB посредством PyMongo и Flask-PyMongo. Документоориентированная модель данных открывает новые возможности для хранения сложноструктурированной информации, особенно в условиях, когда схема данных может эволюционировать.
Хочу подчеркнуть, что выбор системы хранения данных – это не просто технический вопрос, а решение, влияющее на всю архитектуру приложения. Важно проводить этот выбор, исходя из конкретных требований проекта: объема данных, типов запросов, нагрузки и планов масштабирования.
Практические задания по работе с базами данных в Flask
Задание 1: Создание простого приложения с SQLite
Создайте базовое Flask-приложение, которое подключается к SQLite базе данных и создает таблицу users с полями id, name и email. Реализуйте маршрут, который выводит сообщение "База данных успешно подключена".
Задание 2: Чтение данных из базы
Расширьте предыдущее приложение, добавив в него несколько пользователей напрямую через SQL-запросы. Создайте маршрут /users, который выводит список всех пользователей в формате JSON.
Задание 3: Форма для добавления пользователей Создайте веб-форму для добавления новых пользователей в базу данных. Форма должна содержать поля для имени и email, данные должны сохраняться в базе данных SQLite через прямые SQL-запросы.
Работа с различными СУБД
Задание 4: Переход на PostgreSQL Возьмите приложение из задания 3 и модифицируйте его для работы с PostgreSQL вместо SQLite. Обеспечьте возможность переключения между разными базами данных через переменные окружения.
Задание 5: Создание универсального класса для работы с базами данных
Разработайте класс DatabaseManager, который абстрагирует работу с разными СУБД (SQLite, PostgreSQL, MySQL) и позволяет выполнять базовые операции чтения и записи независимо от конкретной базы данных.
SQLAlchemy и Flask-SQLAlchemy
Задание 6: Миграция на Flask-SQLAlchemy
Перепишите приложение из задания 5, используя Flask-SQLAlchemy вместо прямых SQL-запросов. Создайте модель User и реализуйте CRUD-операции с использованием ORM.
Задание 7: Связи между таблицами
Расширьте приложение, добавив модель Post (посты пользователей) с полями id, title, content и user_id. Настройте связь "один-ко-многим" между пользователями и их постами. Реализуйте функциональность добавления постов для конкретных пользователей и отображения всех постов пользователя.
Задание 8: Миграции базы данных
Добавьте в приложение поддержку миграций с помощью Flask-Migrate. Создайте миграцию для добавления поля created_at к модели Post и примените её к базе данных.
NoSQL базы данных и комбинированный подход
Задание 9: Работа с MongoDB Создайте новое Flask-приложение для управления задачами (todo-list), используя MongoDB и Flask-PyMongo. Реализуйте функциональность создания, просмотра, редактирования и удаления задач. Задачи должны иметь поля: название, описание, статус выполнения и дата создания.
Задание 10: Комбинированное использование SQL и NoSQL Разработайте приложение электронного магазина, которое использует PostgreSQL через SQLAlchemy для хранения информации о пользователях, заказах и товарах, а MongoDB для хранения отзывов о товарах и аналитических данных. Реализуйте: - Регистрацию и авторизацию пользователей - Каталог товаров с возможностью фильтрации - Корзину покупок и оформление заказа - Систему отзывов на товары - Страницу истории заказов пользователя