Cat

FastAPI 3. Подключение к SQLAlchemy и генератор сессий

В этом посте продолжим изучение FastAPI. Подключим к проекту SQLAlchemy и создадим фабрику сессий.

Сервис на FastAPI proDream 19 Сентябрь 2024 Просмотров: 1274

В предыдущих постах мы с вами инициализировали проект и создали первоначальную конфигурацию.

Следующим шагом в разработке необходимо настроить подключение к базе данных с использованием SQLAlchemy. Это позволит эффективно управлять сессиями базы данных, выполнять запросы и работать с моделями данных. В процессе мы создадим класс, который будет содержать асинхронный движок базы данных и фабрику для создания сессий.


 

Почему важно использовать асинхронное взаимодействие с базой данных?

Прежде чем перейти к реализации, давайте разберёмся, зачем нужен асинхронный подход к работе с базой данных и как он влияет на производительность вашего приложения.

 

Асинхронные операции: скорость и отзывчивость

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

Асинхронные операции устраняют эту проблему. Вместо того чтобы "застревать" на запросе, приложение может продолжать выполнение других задач, пока база данных обрабатывает запрос. Это позволяет:

  1. Улучшить отзывчивость приложения. Пользователи не заметят задержек, так как приложение не "зависает".
  2. Обработать больше запросов одновременно, что особенно важно для высоконагруженных систем.

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

 

Асинхронный движок и сессии

Асинхронный движок базы данных, создаваемый с помощью функции create_async_engine из SQLAlchemy, позволяет отправлять запросы к базе данных в асинхронном режиме. Это ключевой элемент для работы с асинхронными операциями.

Сессии, создаваемые через async_sessionmaker, обеспечивают управление подключением к базе данных для каждой операции. Использование сессий помогает структурировать работу с базой данных и предоставляет следующие преимущества:

  1. Изоляция запросов. Каждая сессия открывает отдельное подключение к базе данных, что позволяет обрабатывать запросы независимо друг от друга.
  2. Управление ресурсами. Сессия гарантированно закрывается корректно после завершения своей работы. Для этого используется асинхронный контекстный менеджер, который освобождает ресурсы даже в случае возникновения ошибок.
  3. Избежание ошибок. Правильное использование сессий снижает вероятность проблем, таких как:
    • Конкуренция запросов (race conditions).
    • Утечка подключений, возникающая при оставлении сессий открытыми.

 

Асинхронный контекстный менеджер

Асинхронный контекстный менеджер (async with) обеспечивает автоматическое управление временем жизни сессий. Он гарантирует, что каждая сессия базы данных существует строго в рамках одной операции, например, обработки HTTP-запроса.

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

async with async_session() as session:
    # Здесь выполняются запросы к базе данных

 

В этом примере сессия автоматически открывается и закрывается, а в случае возникновения ошибок все изменения откатываются (rollback).

 

Когда это особенно полезно?

Асинхронный подход особенно важен для веб-приложений, которые должны обрабатывать множество одновременных запросов. Примеры таких приложений:

  • API серверы. REST или GraphQL-сервисы, обслуживающие мобильные или фронтенд-приложения.
  • Высоконагруженные системы. Приложения, принимающие тысячи запросов в секунду.
  • Микросервисы. Асинхронность упрощает масштабирование и взаимодействие между компонентами системы.

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


 

Файл db_dependency

На следующем этапе в пакете core, где уже находится файл settings.py, мы создадим новый пакет под названием core_dependency. Этот пакет будет содержать ключевые классы и зависимости, которые понадобятся проекту в будущем. Мы подробно рассмотрим их применение в следующих постах.

Внутри пакета core_dependency создадим файл db_dependency.py. В этом файле мы опишем класс, который будет использоваться как зависимость для подключения и управления сессиями базы данных. Такой подход позволит удобно инжектировать сессию в обработчики запросов или другие компоненты приложения, упрощая доступ к базе данных.


 

Класс DBDependency

В файле db_dependency.py мы создадим класс DBDependency. Этот класс будет включать два ключевых метода: конструктор и метод для предоставления асинхронной сессии.

 

Конструктор класса

Первый метод — это конструктор класса (dunder-метод __init__).

Внутри конструктора создаются два защищённых атрибута:

  1. self._engine — асинхронный движок базы данных, создаваемый с помощью функции create_async_engine. Ему передаются db_url и db_echo из объекта настроек settings.
  2. self._session_factory — фабрика для создания асинхронных сессий, создаваемая с использованием async_sessionmaker. Параметры, такие как autocommit=False и expire_on_commit=False, дают нам полный контроль над управлением транзакциями.

 

Зачем использовать нижнее подчёркивание (_) в именах полей?

Атрибуты self._engine и self._session_factory начинаются с символа _, что сигнализирует, что они являются "защищёнными". Это соглашение в Python означает, что эти поля не предназначены для прямого доступа извне класса. Вместо этого они используются внутри класса или его методов. Этот подход помогает избежать случайного изменения важных данных.

 

Код конструктора:

def __init__(self) -> None:  
    self._engine = create_async_engine(url=settings.db_settings.db_url, echo=settings.db_settings.db_echo)  
    self._session_factory = async_sessionmaker(
        bind=self._engine, 
        expire_on_commit=False, 
        autocommit=False, 
    )

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

 

Метод db_session

Второй ключевой метод класса DBDependency — это db_session, который декорирован с помощью @property. Этот метод возвращает объект фабрики сессий async_sessionmaker, что позволяет удобно управлять сессиями базы данных в приложении.

Метод db_session используется для предоставления сессии базы данных, которую можно применять в асинхронных контекстных менеджерах. Такой подход обеспечивает корректное управление жизненным циклом сессии: она создаётся при начале работы и закрывается автоматически после завершения, даже в случае возникновения ошибок.

 

Логика работы метода:

  1. В приложении создаётся экземпляр класса DBDependency, в который передаются параметры для подключения к базе данных.
  2. Когда необходимо выполнить запрос к базе данных, вызывается метод db_session.
  3. Сессия базы данных используется внутри асинхронного контекстного менеджера.
  4. После завершения работы, контекстный менеджер автоматически закрывает сессию, освобождая ресурсы.

 

Пример использования:

async def get_data_from_db(db_dependency: DBDependency) -> list[dict]:
    async with db_dependency.db_session() as session:
        query = select(MyTable.title, MyTable.created_at)
        result = await session.execute(query)
        return result.mappings().all()

 

Код метода:

@property  
def db_session(self) -> async_sessionmaker[AsyncSession]:  
    return self._session_factory

 

Почему используется @property?

Декоратор @property позволяет обращаться к методу db_session как к атрибуту объекта. Это делает код более читаемым, упрощая работу с зависимостью базы данных. Например:

db_dependency = DBDependency()  
session_factory = db_dependency.db_session  

 

Теперь db_session всегда будет возвращать готовую к использованию фабрику сессий, не требуя дополнительных вызовов.

 

Полный код файла

from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine  

class DBDependency:  
    def __init__(self) -> None:  
        self._engine = create_async_engine(url=settings.db_settings.db_url, echo=settings.db_settings.db_echo)  
        self._session_factory = async_sessionmaker(
            bind=self._engine, 
            expire_on_commit=False, 
            autocommit=False
        )  

    @property  
    def db_session(self) -> async_sessionmaker[AsyncSession]:  
        return self._session_factory

 

Заключение

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

  • Упростить доступ к базе данных. Теперь для выполнения запросов достаточно инжектировать зависимость и работать через асинхронный контекстный менеджер.
  • Избежать утечек подключений. Автоматическое закрытие сессий гарантирует, что ресурсы базы данных освобождаются корректно.
  • Поддерживать асинхронность. Это позволяет приложениям обрабатывать множество запросов одновременно, что критически важно для высоконагруженных API.

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

Репозиторий проекта в GitHub: https://github.com/proDreams/lkeep 
Репозиторий проекта в "GIT на салфетке": https://git.pressanybutton.ru/proDream/lkeep

Автор

    Нет комментариев

    Реклама