В мире современной веб-разработки FastAPI зарекомендовал себя как мощный и быстрый фреймворк для создания API. Однако, при работе над крупными проектами разработчики часто сталкиваются с необходимостью оптимизировать рутинные процессы, улучшить структуру кода и упростить тестирование. В этой статье мы рассмотрим набор инструментов, который поможет решить эти задачи и существенно ускорить разработку на FastAPI.
Ходя по множеству собеседований, я заметил что многие компании, активно использующие FastAPI, разработали собственные библиотеки, но аналогичных инструментов с открытым доступом и свободной лицензией практически нет. Я, как опытный backend-разработчик на Python и Django, решил адаптировать и интегрировать наиболее полезные и востребованные решения для разработки REST API в FastAPI, основываясь на своем опыте работы с Django.
Краткосрочная цель: собрать обратную связь от сообщества о данной идее.
Долгосрочная цель: улучшить инструмент в open source, побуждая крупные компании отказаться от разработки и поддержки собственного проприетарного кода. Вместо этого мы стремимся создать экосистему, где компании не только используют общий инструментарий, но и активно участвуют в его совершенствовании, внося свой вклад в open-source проект.
Для кого это будет полезно?
Backend-разработчикам на Python, использующим или планирующим использовать FastAPI
Командам, работающим над средними и крупными проектами на FastAPI
Разработчикам, которые хотят улучшить структуру своих FastAPI-проектов и ускорить процесс разработки
Тем, кто ищет эффективные инструменты для тестирования FastAPI-приложений
Зачем нужен этот инструментарий?
FastAPI Accelerator - это open-source инструментарий, созданный на основе лучших практик разработки REST API.
Основная цель представленного инструментария - ускорить и упростить разработку проектов на FastAPI.
Это достигается путем:
Подробной и хорошей документацией.
Предоставления переиспользуемого кода для типовых задач.
Внедрения универсального менеджера для работы с РСУБД.
Реализации ViewSet для быстрого создания представлений с базовой бизнес-логикой.
Интеграции аутентификации по JWT.
Упрощения написания и выполнения интеграционных тестов для API.
Оптимизации работы с Alembic для управления миграциями в production и test окружениях.
Все эти компоненты взаимосвязаны и дополняют друг друга, автоматизируя рутинные задачи.
Структура инструмента
Давайте рассмотрим основные компоненты нашего инструментария:
fastapi_accelerator/
├── db/ # Логика взаимодействия с РСУБД
├── pattern/ # Шаблоны для проектов
├── testutils/ # Утилиты для тестирования FastAPI
├── cache.py # Реализация кеширования
├── auth_jwt.py # Аутентификация по JWT
├── exception.py # Обработка исключений
├── middleware.py # Middleware компоненты
├── paginator.py # Реализация пагинации
├── timezone.py # Работа с временными зонами
├── viewset.py # Реализация ViewSet
└── utils.py # Общие утилиты
Структуры проекта FastAPI
Правильная организация проекта - ключ к его масштабируемости и удобству поддержки. Вот пример рекомендуемой структуры проекта FastAPI с использованием FastAPI Accelerator:
Проект/
│
├── app/
│ ├── __init__.py
│ ├── utils.py # Пере используемый функционал для проекта
│ ├── core/ # Содержит основные модули, такие как конфигурация, безопасность и общие зависимости.
│ │ ├── __init__.py
│ │ ├── settings_local.py # Локальные значения для настроек, не должны быть в git, создавать непосредственно на сервере
│ │ ├── config.py # Настройки проекта которые не зависят от внешних настроек
│ │ ├── security.py # Логика безопасности проекта
│ │ ├── db.py # Настройки и сессии базы данных.
│ │ ├── cache.py # Настройки кеширования
│ │ └── dependencies.py
│ │
│ ├── api/ # Содержит все API эндпоинты, разделенные по версиям.
│ │ ├── __init__.py
│ │ └── v1/
│ │ ├── __init__.py
│ │ ├── router.py # Содержит обработчики запросов для указанной версии api
│ │ │
│ │ ├── static/ # Содержит файлы статики, если они нужны
│ │ │ ├── js
│ │ │ ├── css
│ │ │ ├── img
│ │ │ └── html
│ │ │
│ │ ├── logic/ # Содержит бизнес логику
│ │ │ ├── __init__.py
│ │ │ ├── users.py
│ │ │ └── items.py
│ │ │
│ │ ├── schemas/ # Pydantic модели для валидации запросов и ответов.
│ │ │ ├── __init__.py
│ │ │ ├── user.py
│ │ │ └── item.py
│ │ │
│ │ ├── crud/ # Функции для работы с базой данных (Create, Read, Update, Delete).
│ │ │ ├── __init__.py
│ │ │ ├── user.py
│ │ │ └── item.py
│ │ │
│ │ └── tests/ # Директория для тестов.
│ │ ├── __init__.py
│ │ ├── test_users.py
│ │ └── test_items.py
│ │
│ ├── models/ # Определения моделей базы данных (например, SQLAlchemy модели).
│ │ ├── __init__.py
│ │ ├── user.py
│ │ └── item.py
│ │
│ └── fixture/ # Хранит фикстуры для тестирования этого проекта
│ ├── __init__.py
│ ├── items_v1.py # Тестовые записи для БД
│ └── utils.py # Переиспользуемые фикстуры для тестов
│
├── fastapi_accelerator/ # Submodule для переиспользовать
│
├── alembic/ # Директория для миграций базы данных.
│ ├── versions/ # Папка с миграциями
│ │ ├── __init__.py
│ │ └── 0001_init.py # Файл с миграцией
│ └── env.py # Настройки для alembic
│
├─ conf/ # Файлы конфигурации для prod
│ ├── settings_local.example.py # Пример для создания settings_local.py
│ └── Dockerfile # Файл для prod
│
├── pytest.ini # Конфигурация для pytest
├── conftest.py # Настройки выполнения тестов
│
├── .gitignore # Какие игнорировать файлы и папки в git
├── .gitlab-ci.yml # Настройки CI pipeline
│
├── pyproject.toml # Настройки Poetry
│
├── Makefile # Переиспользуемые bash команды
│
├── README.md # Описание проекта
├── CHANGELOG.md # Изменения в проекте
├── version.toml # Версия проекта
│
├── alembic.ini # Конфигурации для alembic
│
├── DockerfileDev # Файл для создания dev контейнера с APP
├── docker-compose.yml # Используется для сборки dev окружения
│
├── admin_panel.py # Админ панель
│
└── main.py # Точка входа в приложение, где создается экземпляр FastAPI.
Подключение в FastAPI
Файл main.py
:
from fastapi import FastAPI
from fastapi_accelerator.pattern.pattern_fastapi import base_pattern
from app.core.config import BASE_DIR_PROJECT, DEBUG, SECRET_KEY
from fastapi_accelerator.timezone import moscow_tz
from app.core.db import DatabaseManager
from app.core.security import AuthJWT
import app.api.v1.router as RouterV1
app = FastAPI()
# Паттерн для проекта
base_pattern(
app,
routers=(RouterV1.router,),
timezone=moscow_tz,
cache_status=True,
debug=DEBUG,
base_dir=BASE_DIR_PROJECT,
database_manager=DatabaseManager,
secret_key=SECRET_KEY,
)
# Подключить аутентификацию по JWT
AuthJWT.mount_auth(app)
Основные компоненты
Base Pattern
Функция base_pattern
добавляет множество полезных функций в app
, включая:
Заполнение
state
и другую информацию уapp
.Разрешение
CORS
.Подключение роутеров с поддержкой
ViewSet
.Добавление метода
healthcheck
.Middleware
для отладки времени выполнения API-запросов.Подробный вывод для
HTTP
исключений.
DatabaseManager
DatabaseManager
- это универсальный инструмент для работы с РСУБД, предоставляющий как синхронные, так и асинхронные(название начинается на a
) методы. DatabaseManager
использует патер одиночка, поэтому может быть легко подменен в тестах.
Пример использования:
from app.core.config import DATABASE_URL, DEBUG, DEV_STATUS
from fastapi_accelerator.dbsession import MainDatabaseManager
DatabaseManager = MainDatabaseManager(DATABASE_URL, echo=DEBUG, DEV_STATUS=DEV_STATUS)
-
Общие характеристики
DEV_STATUS
- Индикатор режима разработки. ПриDEV_STATUS=False
блокирует выполнение критических операций (create_all
,drop_all
,clear_all
). Это мера безопасности для производственной среды.
-
Синхронные компоненты
database_url
- Адрес для подключения к синхронной базе данных.engine
- Механизм синхронного взаимодействия с БД.session
- Генератор синхронных сессий.Base
- Базовый класс для моделей данных.-
Функциональность:
get_session
- Инжектор сессии БД.get_session_transaction
- Инжектор сессии БД с поддержкой транзакций.create_all
- Инициализация всех таблиц в БД.drop_all
- Удаление всей структуры БД.clear_all
- Очистка содержимого таблиц. Параметрexclude_tables_name
позволяет исключить определенные таблицы из процесса очистки.
-
Асинхронные компоненты
adatabase_url
- Адрес для подключения к асинхронной БД.aengine
- Асинхронный механизм работы с БД, включая пул соединений.asession
- Генератор асинхронных сессий.-
Функциональность:
aget_session
- Асинхронный инжектор сессии БД.aget_session_transaction
- Асинхронный инжектор сессии БД с поддержкой транзакций.
OrmAsync
Этот класс оптимизирует асинхронное взаимодействие с БД:
get
- Извлечение объекта по заданным критериям.get_list
- Получение набора объектов по запросу. (С возможностью глубокой выборки)update
- Модификация объектов согласно запросу.delete
- Удаление объектов по заданным параметрам.get_item
- Извлечение объекта по первичному ключу. (С возможностью глубокой выборки)create_item
- Создание нового объекта. (С возможностью каскадного создания)update_item
- Обновление объекта по первичному ключу. (С возможностью каскадного обновления)delete_item
- Удаление объекта по первичному ключу. (С возможностью каскадного удаления)
Глубокая выборка/каскадные операции - это возможность работы со связанными данными.
Активируется параметромdeep=True
Примеры:
get_list, get_item - Возвращают объекты со всеми связанными данными, готовые для использования в Pydantic
create_item - Создает записи в связанных таблицах
update_item - Обновляет данные в связанных таблицах
delete_item - Удаляет записи из связанных таблиц
ViewSet
ViewSet позволяет быстро создавать CRUD-операции для моделей. Вот пример использования:
from fastapi_accelerator.viewset import AppOrm, FullViewSet
from fastapi import APIRouter, Depends, Query
from app.api.v1.schemas.timemeasurement import TaskExecution
from app.models.timemeasurement import TaskExecution as TaskExecutionDb
router = APIRouter(prefix="/api/v1")
class FileViewSet(FullViewSet):
"""
Представление для работы с файлами
"""
# Модель БД
db_model = TaskExecutionDb
# Модель Схемы
pydantic_model = TaskExecution
'''
# Кеширование
cache_class = redis_client
cache_ttl = timedelta(minutes=10)
# Пагинация
paginator_class = DefaultPaginator
# Включить поддержку вложенных схем pydantic
# это значит что будет происходить рекурсивное
# создание, обновление, удаление связанных записей
deep_schema = True
# Включить защиту через JWT
dependencies = [Depends(jwt_auth)]
# Вы можете также переопределять методы:
async def db_update(
self, item_id: str | int | UUID, item: type[BaseModel], aorm: OrmAsync
) -> object:
"""Переопределение метода db_update"""
return await super().db_update(item_id, item, aorm)
def list(self):
"""Переопределение метода list"""
@self.router.get(f"{self.prefix}", tags=self.tags)
async def get_list_items(
skip: int = Query(0),
limit: int = Query(100),
aorm: OrmAsync = Depends(AppOrm.aget_orm),
) -> List[self.pydantic_model]:
return await aorm.get_list(
self.db_model,
select(self.db_model).offset(skip).limit(limit),
deep=self.deep_schema,
)
return get_list_items
'''
router.views = [
FileViewSet().as_view(router, prefix="/file"),
]
Аутентификация по JWT
Для защиты API-эндпоинтов мы используем JWT-аутентификацию:
from fastapi_accelerator.auth_jwt import BaseAuthJWT
class AuthJWT(BaseAuthJWT):
def check_auth(username: str, password: str) -> bool:
"""Проверка введенного логина и пароля."""
return username == "admin" and password == "admin"
AuthJWT.mount_auth(app)
Пример защиты API метода:
from fastapi_accelerator.auth_jwt import jwt_auth
@app.get("/check_protected", summary="Проверить аутентификацию по JWT")
async def protected_route(jwt: dict = Depends(jwt_auth)):
return {"message": "This is a protected route", "user": jwt}
Тестирование
Одной из ключевых особенностей нашего инструментария является мощная система для написания и выполнения тестов. Она включает в себя:
Фикстуры для работы с тестовой базой данных и клиентом API.
Декораторы для аутентификации и применения фикстур.
Контекстный менеджер для отслеживания SQL-запросов.
Утилиты для проверки JSON-ответов.
Тестирование через классы.
Пример функции теста:
from typing import Callable, NamedTuple
from fastapi.testclient import TestClient
from app.fixture.items_v1 import export_fixture_file
from fastapi_accelerator.db.dbsession import MainDatabaseManager
from fastapi_accelerator.testutils import apply_fixture_db, client_auth_jwt, track_queries, check_response_json
# Аутентифицировать тестового клиента
@client_auth_jwt(username="test")
# Создать тестовые данных из функции с фикстурами
@apply_fixture_db(export_fixture_file)
def test_имя(
client: TestClient, # Тестовый клиент для API запросов
url_path_for: Callable, # Функция для получения url по имени функции обработчика
db_manager: MainDatabaseManager, # Менеджер тестовой БД
fixtures: NamedTuple, # Хранит созданные данные из фикстур
):
# Проверка количество выполняемых SQL команд
with track_queries(db_manager, expected_count=3):
# Запрос в API
response = client.get(url_path_for("ИмяФункции"))
# Проверка JSON API ответа
check_response_json(
response,
200,
{
"id": fixtures.Имя.id,
},
)
Пример тест класса:
from typing import Callable, NamedTuple
from fastapi.testclient import TestClient
from app.fixture.items_v1 import export_fixture_file
from fastapi_accelerator.db.dbsession import MainDatabaseManager
from fastapi_accelerator.testutils import apply_fixture_db
from fastapi_accelerator.testutils.fixture_auth import client_auth_jwt
from fastapi_accelerator.testutils.fixture_db.trace_sql import track_queries
from fastapi_accelerator.testutils.utils import BaseAuthJwtPytest, check_response_json
BASE_URL_V1 = "/api/v1/"
class TestИмя(BaseAuthJwtPytest):
# Создать тестовые данных из функции с фикстурами
@apply_fixture_db(export_fixture_file)
def setUp(self, fixtures: NamedTuple):
self.url = BASE_URL_V1 + "taskexecution"
self.fixtures = fixtures # Хранит созданные данные из фикстур
def test_имя(self, client: TestClient, db_manager: MainDatabaseManager):
# Проверка количество выполняемых SQL команд
with track_queries(db_manager, expected_count=3):
# Запрос в API
response = client.get(self.url)
# Проверка JSON API ответа
check_response_json(
response,
200,
{
"id": self.fixtures.Имя.id,
},
)
Сравнение с существующими решениями
Хотя существует несколько проектов, предлагающих инструменты для разработки и тестирования FastAPI приложений, наше решение выделяется своей комплексностью и специализацией:
FastAPI-Utils
: Предоставляет утилиты для разработки, но менее фокусирован на тестировании.FastAPI-SQLAlchemy
: Интегрирует FastAPI с SQLAlchemy, включая некоторые утилиты для тестирования.FastAPI-Toolkit
: Предлагает набор инструментов, но менее специализирован на задачах тестирования.freddie
- В архиве на GitHub, только viewsetfastapi_viewsets
- Только viewsetFastAPIwee
- Менее специализирован на задачах тестирования.
Наше решение отличается тем, что:
Более специфично для задач тестирования FastAPI приложений.
Предоставляет более широкий набор инструментов для различных аспектов тестирования.
Включает уникальные функции, такие как декоратор
@apply_fixture_db
и контекстный менеджерtrack_queries
.Предлагает комплексный подход, охватывающий различные аспекты разработки и тестирования FastAPI приложений.
Заключение
Представленный инструментарий значительно упрощает и ускоряет разработку на FastAPI. Он предоставляет готовые решения для типовых задач, улучшает структуру проекта и облегчает тестирование. Использование этих инструментов позволит разработчикам сосредоточиться на бизнес-логике приложения, а не на технических деталях реализации.
Несмотря на наличие других инструментов в экосистеме FastAPI, наше решение выделяется своей полнотой и специализацией именно на задачах тестирования. Это делает его ценным дополнением к существующим ресурсам для разработчиков FastAPI.
Мы продолжаем развивать этот инструментарий и будем рады обратной связи от сообщества. Если у вас есть идеи по улучшению или вы нашли ошибку, пожалуйста, создайте issue в нашем репозитории на GitHub.
Планы развития
Обеспечить совместимость с последними(прошлыми) версиями FastAPI и связанных библиотек
Значительно увеличить покрытие кода тестами
Оптимизировать существующий код для повышения производительности
Провести нагрузочное тестирование и оптимизировать критические участки кода
Провести рефакторинг с учетом лучших практик и паттернов проектирования
Разработать и добавить аналогичный вариант для работы с
WebSocket
Усовершенствовать механизм выполнения фоновых задач (
background tasks
)Улучшить реализацию периодических задач, подобных тем, что представлены в
fastapi-utils
Создать несколько детальных примеров проектов, демонстрирующих различные сценарии использования
Расширить документацию, добавив больше практических руководств и рекомендаций по применению
Исследовать возможности интеграции с популярными инструментами экосистемы FastAPI
Комментарии (8)
danilovmy
08.09.2024 13:02решил адаптировать и интегрировать наиболее полезные и востребованные решения основываясь на своем опыте работы с Django.
Я уж настроился... и тут, неожиданно:
Админ-панель. Для удобного управления данными мы интегрировали Flask-Admin
а чего так то? :(
P.s. Лайкнул, потому как сам использую подобные идеи. Рекомендую подглядеть, как в Django сделали async as_view(), и еще можно подглядеть, как реализован CBV в упомянутых FastApi-Utils
lazy_val
08.09.2024 13:02Для удобного управления данными мы интегрировали Flask-Admin
а чего так то? :(
Против джипити не попрешь ... ))
ivankudryavtsev
С GitHub на русском языке светлого будущего, боюсь, что не случится… Даже на Reddit в r/Python не сможете промоутить. Рынок русскоязычных разработчиков не велик.
Ну и комментариями на русском тоже. Я вижу что у Вас это второй «стандарт первый в мире, второй по Сибири». Если есть такие амбиции, то заход явно не эффективен.
denisxab Автор
Вы правы, что глобальное продвижение русскоязычного проекта на GitHub может быть затруднительным. Однако основная цель размещения кода на этой платформе - не столько широкое распространение, сколько удобство разработки и возможность получения обратной связи от русскоязычного сообщества. Перевести весь проект на английский язык - дело одного промта для GPT.
ivankudryavtsev
Вы явно недооцениваете сложность работы с сообществом. GPT тут так себе помощник.
denisxab Автор
В настоящее время я сосредоточен на взаимодействии с русскоговорящими разработчиками, так как это мне ближе и понятнее. Моя стратегия заключается в том, чтобы сначала оптимизировать код, а затем с помощью GPT перевести документацию и комментарии на английский язык. Завершающим этапом станет публикация статьи на Reddit и r/Python, что позволит мне "эффективно" выйти на международный рынок разработки.
ivankudryavtsev
Были бы мы на Пикабу, я бы написал - делайте скриншот этой переписки.