# dbalchemycore
**dbalchemycore** — это асинхронная библиотека для работы с базой данных PostgreSQL через SQLAlchemy Core. Она предоставляет удобный интерфейс для выполнения CRUD-операций, управления подключением к базе данных, миграциями через Alembic и асинхронными сессиями. Библиотека ориентирована на производительность, минимизацию дублирования кода и интеграцию с FastAPI и другими асинхронными фреймворками.
## Основные возможности
- **Асинхронные CRUD-операции**: Поддержка создания, чтения, обновления и удаления записей через обобщённый класс `BaseRepository`.
- **Pydantic-валидация**: Использование Pydantic-схем для фильтров, значений и агрегаций.
- **Поддержка миграций**: Интеграция с Alembic для управления схемой базы данных.
- **Управление транзакциями**: Декоратор `connection()` для автоматического управления асинхронными сессиями SQLAlchemy.
- **Гибкость**: Поддержка фильтрации, сортировки, пагинации, группировки и агрегаций (`count`, `sum`, `avg`, `min`, `max`).
- **Логирование**: Встроенное логирование операций и SQL-запросов для отладки.
## Установка
```bash
pip install dbalchemycore
```
Требуемые зависимости:
- `sqlalchemy[asyncpg]`
- `pydantic`
- `pydantic-settings`
- `alembic`
## Структура проекта
```text
project/
├── migration/ # Директория для миграций Alembic
│ ├── versions/ # Файлы миграций
│ └── alembic.ini # Конфигурация Alembic
├── dbAlchemyCore/ # Модуль библиотеки
│ ├── __init__.py # Инициализация (init_db)
│ ├── models/ # Модели SQLAlchemy
│ ├── schemas/ # Pydantic-схемы
│ ├── repositories/ # Репозитории для CRUD
│ ├── services/ # Бизнес-логика
│ └── utils/ # Утилиты
├── .env # Переменные окружения
└── main.py # Точка входа
```
## Начало работы
1. **Настройка окружения**:
Создайте `.env` файл с параметрами подключения к базе данных:
```env
DB_USER=postgres
DB_PASSWORD=secret
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mydb
DB_DRIVER=asyncpg
DB_DIALECT=postgresql
DB_POOL_SIZE=20
DB_MAX_OVERFLOW=50
DB_ECHO=False
DB_CONNECT_TIMEOUT=10
```
2. **Инициализация базы данных**:
Вызовите `init_db` один раз при старте приложения:
```python
import asyncio
from dbAlchemyCore import init_db
async def main():
await init_db(migrations_path="alembic")
if __name__ == "__main__":
asyncio.run(main())
```
3. **Создание модели**:
Модели должны наследоваться от `Base` и использовать `Mapped` для полей:
```python
# dbAlchemyCore/models/user.py
from sqlalchemy.orm import Mapped, mapped_column
from dbAlchemyCore import Base
class User(Base):
name: Mapped[str] = mapped_column(nullable=False)
email: Mapped[str] = mapped_column(unique=True, nullable=False)
```
4. **Создание Pydantic-схем**:
Определите схемы для валидации данных:
```python
# dbAlchemyCore/schemas/user_schemas.py
from pydantic import BaseModel
from typing import Optional
class UserCreate(BaseModel):
name: str
email: str
class UserFilter(BaseModel):
name: Optional[str] = None
email: Optional[str] = None
```
5. **Создание репозитория**:
Создайте класс репозитория, унаследованный от `BaseRepository`:
```python
# dbAlchemyCore/repositories/user_repo.py
from dbAlchemyCore.models.user import User
from dbAlchemyCore.repositories import BaseRepository
class UserRepo(BaseRepository):
model = User
```
6. **Использование**:
Пример выполнения CRUD-операций:
```python
# main.py
import asyncio
from dbAlchemyCore import init_db
from dbAlchemyCore.repositories.user_repo import UserRepo
from dbAlchemyCore.schemas.user_schemas import UserCreate, UserFilter
from sqlalchemy.ext.asyncio import AsyncSession
async def main():
await init_db(migrations_path="alembic")
# Создание пользователя
user_data = UserCreate(name="John", email="john@example.com")
await UserRepo.create(values=user_data, session=session)
# Получение пользователя
user = await UserRepo.get_one(filters=UserFilter(name="John"), session=session)
print(user) # {"id": 1, "name": "John", "email": "john@example.com"}
if __name__ == "__main__":
asyncio.run(main())
```
## Основные компоненты
### `init_db`
Инициализирует подключение к базе данных, создаёт движок `AsyncEngine` и фабрику сессий `async_sessionmaker`. Применяет миграции Alembic или создаёт таблицы.
**Параметры**:
- `migrations_path: str` — путь к директории миграций (например, `"alembic"`).
**Исключения**:
- `ConnectionRefusedError`: Сервер базы данных недоступен.
- `InvalidPasswordError`: Неверный пароль.
- `InvalidCatalogNameError`: Несуществующая база данных.
- `OperationalError`, `DatabaseError`, `InvalidRequestError`: Ошибки подключения SQLAlchemy.
- `CommandError`, `FileNotFoundError`: Ошибки миграций Alembic.
### `Base`
Базовый класс для моделей, автоматически добавляет поле `id` (первичный ключ) и генерирует имя таблицы (например, `users` для класса `User`).
### `BaseRepository`
Обобщённый класс для CRUD-операций. Поддерживает:
- **Методы**: `get_one`, `get_many`, `create`, `update`, `delete`, `execute_sql`.
- **Фильтры**: Через Pydantic-схемы, поддержка `IN` для списков (например, `role=["admin", "moderator"]`).
- **Сортировка**: Поддержка `order_by` с префиксом `-` для DESC.
- **Пагинация**: Параметры `limit` и `offset`.
- **Группировка и агрегация**: `group_by`, `aggregations` (`count`, `sum`, `avg`, `min`, `max`), `having_filters`.
**Исключения**:
- `NotFoundError`: Запись не найдена.
- `EmptyFilterError`: Пустые фильтры.
- `InvalidFieldError`: Несуществующее поле.
- `EmptyValueError`: Пустые значения для обновления.
- `UnknowAggregationFunc`: Неверная функция агрегации.
- `MultipleResultsFound`: Найдено несколько записей при `strict=True` в `get_one`.
### `connection()`
Декоратор для управления асинхронными сессиями в кастомных методах. Поддерживает настройку уровня изоляции (`isolation_level`) и коммита (`commit`).
**Пример**:
```python
from dbAlchemyCore.repositories import BaseRepository, connection
from sqlalchemy.ext.asyncio import AsyncSession
class UserRepo(BaseRepository):
model = User
@classmethod
@connection(commit=True)
async def get_user_orm(cls, id: int, session: AsyncSession):
return await session.get(cls.model, id)
```
## Рекомендации
- **Модели**: Наследуйте от `Base`, используйте `Mapped` для полей.
- **Репозитории**: Создавайте отдельный класс для каждой модели, указывайте `model`.
- **Pydantic-схемы**: Используйте для валидации фильтров и значений, делайте поля опциональными для фильтрации/обновления.
- **Транзакции**: Применяйте `@connection()` для кастомных методов с доступом к базе.
- **Бизнес-логика**: Выносите сложную логику в модуль `services`.
- **Тестирование**: Используйте `pytest-asyncio` для асинхронных тестов, настройте тестовую БД (например, SQLite в памяти).
## Примечания
- **Асинхронность**: Все операции асинхронные, используют `AsyncSession` и `asyncpg`.
- **Ограничения**: `HAVING` требует `GROUP BY` (ограничение SQL). Фильтры не поддерживают сравнения (`>`, `<`, и т.д.).
- **Производительность**: Используйте SQLAlchemy Core для bulk-операций, избегайте смешивания с ORM для предотвращения stale data.
Raw data
{
"_id": null,
"home_page": null,
"name": "dbalchemycore",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "database, async, sqlalchemy, postgresql, crud, alembic, pydantic, fastapi",
"author": null,
"author_email": "tktturik <tktturik@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/8f/82/e98525fd8b0396b5011e8be5866a304b145dfabe398a29e8037d08b914b2/dbalchemycore-0.1.3.tar.gz",
"platform": null,
"description": "# dbalchemycore\r\n\r\n**dbalchemycore** \u2014 \u044d\u0442\u043e \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 PostgreSQL \u0447\u0435\u0440\u0435\u0437 SQLAlchemy Core. \u041e\u043d\u0430 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f CRUD-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439, \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c \u043a \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445, \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f\u043c\u0438 \u0447\u0435\u0440\u0435\u0437 Alembic \u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c\u0438 \u0441\u0435\u0441\u0441\u0438\u044f\u043c\u0438. \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430 \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c, \u043c\u0438\u043d\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u044e \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0441 FastAPI \u0438 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c\u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430\u043c\u0438.\r\n\r\n## \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438\r\n\r\n- **\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 CRUD-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438**: \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f, \u0447\u0442\u0435\u043d\u0438\u044f, \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0447\u0435\u0440\u0435\u0437 \u043e\u0431\u043e\u0431\u0449\u0451\u043d\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 `BaseRepository`.\r\n- **Pydantic-\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f**: \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 Pydantic-\u0441\u0445\u0435\u043c \u0434\u043b\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432, \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0438 \u0430\u0433\u0440\u0435\u0433\u0430\u0446\u0438\u0439.\r\n- **\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0439**: \u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 Alembic \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0445\u0435\u043c\u043e\u0439 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.\r\n- **\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f\u043c\u0438**: \u0414\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 `connection()` \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c\u0438 \u0441\u0435\u0441\u0441\u0438\u044f\u043c\u0438 SQLAlchemy.\r\n- **\u0413\u0438\u0431\u043a\u043e\u0441\u0442\u044c**: \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438, \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438, \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438, \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0438 \u0438 \u0430\u0433\u0440\u0435\u0433\u0430\u0446\u0438\u0439 (`count`, `sum`, `avg`, `min`, `max`).\r\n- **\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435**: \u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0435 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 \u0438 SQL-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0434\u043b\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0438.\r\n\r\n## \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430\r\n\r\n```bash\r\npip install dbalchemycore\r\n```\r\n\r\n\u0422\u0440\u0435\u0431\u0443\u0435\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:\r\n- `sqlalchemy[asyncpg]`\r\n- `pydantic`\r\n- `pydantic-settings`\r\n- `alembic`\r\n\r\n## \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\r\n\r\n```text\r\nproject/\r\n\u251c\u2500\u2500 migration/ # \u0414\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f \u0434\u043b\u044f \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0439 Alembic\r\n\u2502 \u251c\u2500\u2500 versions/ # \u0424\u0430\u0439\u043b\u044b \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0439\r\n\u2502 \u2514\u2500\u2500 alembic.ini # \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f Alembic\r\n\u251c\u2500\u2500 dbAlchemyCore/ # \u041c\u043e\u0434\u0443\u043b\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438\r\n\u2502 \u251c\u2500\u2500 __init__.py # \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f (init_db)\r\n\u2502 \u251c\u2500\u2500 models/ # \u041c\u043e\u0434\u0435\u043b\u0438 SQLAlchemy\r\n\u2502 \u251c\u2500\u2500 schemas/ # Pydantic-\u0441\u0445\u0435\u043c\u044b\r\n\u2502 \u251c\u2500\u2500 repositories/ # \u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u0434\u043b\u044f CRUD\r\n\u2502 \u251c\u2500\u2500 services/ # \u0411\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430\r\n\u2502 \u2514\u2500\u2500 utils/ # \u0423\u0442\u0438\u043b\u0438\u0442\u044b\r\n\u251c\u2500\u2500 .env # \u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f\r\n\u2514\u2500\u2500 main.py # \u0422\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430\r\n```\r\n\r\n## \u041d\u0430\u0447\u0430\u043b\u043e \u0440\u0430\u0431\u043e\u0442\u044b\r\n\r\n1. **\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f**:\r\n\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 `.env` \u0444\u0430\u0439\u043b \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445:\r\n\r\n```env\r\nDB_USER=postgres\r\nDB_PASSWORD=secret\r\nDB_HOST=localhost\r\nDB_PORT=5432\r\nDB_NAME=mydb\r\nDB_DRIVER=asyncpg\r\nDB_DIALECT=postgresql\r\nDB_POOL_SIZE=20\r\nDB_MAX_OVERFLOW=50\r\nDB_ECHO=False\r\nDB_CONNECT_TIMEOUT=10\r\n```\r\n\r\n2. **\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445**:\r\n\u0412\u044b\u0437\u043e\u0432\u0438\u0442\u0435 `init_db` \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u043f\u0440\u0438 \u0441\u0442\u0430\u0440\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f:\r\n\r\n```python\r\nimport asyncio\r\nfrom dbAlchemyCore import init_db\r\n\r\nasync def main():\r\n await init_db(migrations_path=\"alembic\")\r\n\r\nif __name__ == \"__main__\":\r\n asyncio.run(main())\r\n```\r\n\r\n3. **\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043c\u043e\u0434\u0435\u043b\u0438**:\r\n\u041c\u043e\u0434\u0435\u043b\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0442 `Base` \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c `Mapped` \u0434\u043b\u044f \u043f\u043e\u043b\u0435\u0439:\r\n\r\n```python\r\n# dbAlchemyCore/models/user.py\r\nfrom sqlalchemy.orm import Mapped, mapped_column\r\nfrom dbAlchemyCore import Base\r\n\r\nclass User(Base):\r\n name: Mapped[str] = mapped_column(nullable=False)\r\n email: Mapped[str] = mapped_column(unique=True, nullable=False)\r\n```\r\n\r\n4. **\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 Pydantic-\u0441\u0445\u0435\u043c**:\r\n\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u0435 \u0441\u0445\u0435\u043c\u044b \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445:\r\n\r\n```python\r\n# dbAlchemyCore/schemas/user_schemas.py\r\nfrom pydantic import BaseModel\r\nfrom typing import Optional\r\n\r\nclass UserCreate(BaseModel):\r\n name: str\r\n email: str\r\n\r\nclass UserFilter(BaseModel):\r\n name: Optional[str] = None\r\n email: Optional[str] = None\r\n```\r\n\r\n5. **\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f**:\r\n\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043a\u043b\u0430\u0441\u0441 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f, \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043e\u0442 `BaseRepository`:\r\n\r\n```python\r\n# dbAlchemyCore/repositories/user_repo.py\r\nfrom dbAlchemyCore.models.user import User\r\nfrom dbAlchemyCore.repositories import BaseRepository\r\n\r\nclass UserRepo(BaseRepository):\r\n model = User\r\n```\r\n\r\n6. **\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435**:\r\n\u041f\u0440\u0438\u043c\u0435\u0440 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f CRUD-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439:\r\n\r\n```python\r\n# main.py\r\nimport asyncio\r\nfrom dbAlchemyCore import init_db\r\nfrom dbAlchemyCore.repositories.user_repo import UserRepo\r\nfrom dbAlchemyCore.schemas.user_schemas import UserCreate, UserFilter\r\nfrom sqlalchemy.ext.asyncio import AsyncSession\r\n\r\nasync def main():\r\n await init_db(migrations_path=\"alembic\")\r\n\r\n # \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\r\n user_data = UserCreate(name=\"John\", email=\"john@example.com\")\r\n await UserRepo.create(values=user_data, session=session)\r\n\r\n # \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\r\n user = await UserRepo.get_one(filters=UserFilter(name=\"John\"), session=session)\r\n print(user) # {\"id\": 1, \"name\": \"John\", \"email\": \"john@example.com\"}\r\n\r\nif __name__ == \"__main__\":\r\n asyncio.run(main())\r\n```\r\n\r\n## \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b\r\n\r\n### `init_db`\r\n\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445, \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0434\u0432\u0438\u0436\u043e\u043a `AsyncEngine` \u0438 \u0444\u0430\u0431\u0440\u0438\u043a\u0443 \u0441\u0435\u0441\u0441\u0438\u0439 `async_sessionmaker`. \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 Alembic \u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u044b.\r\n\r\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b**:\r\n- `migrations_path: str` \u2014 \u043f\u0443\u0442\u044c \u043a \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0439 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, `\"alembic\"`).\r\n\r\n**\u0418\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f**:\r\n- `ConnectionRefusedError`: \u0421\u0435\u0440\u0432\u0435\u0440 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d.\r\n- `InvalidPasswordError`: \u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c.\r\n- `InvalidCatalogNameError`: \u041d\u0435\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445.\r\n- `OperationalError`, `DatabaseError`, `InvalidRequestError`: \u041e\u0448\u0438\u0431\u043a\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f SQLAlchemy.\r\n- `CommandError`, `FileNotFoundError`: \u041e\u0448\u0438\u0431\u043a\u0438 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0439 Alembic.\r\n\r\n### `Base`\r\n\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u043c\u043e\u0434\u0435\u043b\u0435\u0439, \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0435 `id` (\u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 \u043a\u043b\u044e\u0447) \u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0438\u043c\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, `users` \u0434\u043b\u044f \u043a\u043b\u0430\u0441\u0441\u0430 `User`).\r\n\r\n### `BaseRepository`\r\n\u041e\u0431\u043e\u0431\u0449\u0451\u043d\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f CRUD-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439. \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442:\r\n- **\u041c\u0435\u0442\u043e\u0434\u044b**: `get_one`, `get_many`, `create`, `update`, `delete`, `execute_sql`.\r\n- **\u0424\u0438\u043b\u044c\u0442\u0440\u044b**: \u0427\u0435\u0440\u0435\u0437 Pydantic-\u0441\u0445\u0435\u043c\u044b, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 `IN` \u0434\u043b\u044f \u0441\u043f\u0438\u0441\u043a\u043e\u0432 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, `role=[\"admin\", \"moderator\"]`).\r\n- **\u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0430**: \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 `order_by` \u0441 \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c `-` \u0434\u043b\u044f DESC.\r\n- **\u041f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u044f**: \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b `limit` \u0438 `offset`.\r\n- **\u0413\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0430 \u0438 \u0430\u0433\u0440\u0435\u0433\u0430\u0446\u0438\u044f**: `group_by`, `aggregations` (`count`, `sum`, `avg`, `min`, `max`), `having_filters`.\r\n\r\n**\u0418\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f**:\r\n- `NotFoundError`: \u0417\u0430\u043f\u0438\u0441\u044c \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430.\r\n- `EmptyFilterError`: \u041f\u0443\u0441\u0442\u044b\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u044b.\r\n- `InvalidFieldError`: \u041d\u0435\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0435 \u043f\u043e\u043b\u0435.\r\n- `EmptyValueError`: \u041f\u0443\u0441\u0442\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f.\r\n- `UnknowAggregationFunc`: \u041d\u0435\u0432\u0435\u0440\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0430\u0433\u0440\u0435\u0433\u0430\u0446\u0438\u0438.\r\n- `MultipleResultsFound`: \u041d\u0430\u0439\u0434\u0435\u043d\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u043f\u0440\u0438 `strict=True` \u0432 `get_one`.\r\n\r\n### `connection()`\r\n\u0414\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c\u0438 \u0441\u0435\u0441\u0441\u0438\u044f\u043c\u0438 \u0432 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u0430\u0445. \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0443\u0440\u043e\u0432\u043d\u044f \u0438\u0437\u043e\u043b\u044f\u0446\u0438\u0438 (`isolation_level`) \u0438 \u043a\u043e\u043c\u043c\u0438\u0442\u0430 (`commit`).\r\n\r\n**\u041f\u0440\u0438\u043c\u0435\u0440**:\r\n```python\r\nfrom dbAlchemyCore.repositories import BaseRepository, connection\r\nfrom sqlalchemy.ext.asyncio import AsyncSession\r\n\r\nclass UserRepo(BaseRepository):\r\n model = User\r\n\r\n @classmethod\r\n @connection(commit=True)\r\n async def get_user_orm(cls, id: int, session: AsyncSession):\r\n return await session.get(cls.model, id)\r\n```\r\n\r\n## \u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0430\u0446\u0438\u0438\r\n\r\n- **\u041c\u043e\u0434\u0435\u043b\u0438**: \u041d\u0430\u0441\u043b\u0435\u0434\u0443\u0439\u0442\u0435 \u043e\u0442 `Base`, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 `Mapped` \u0434\u043b\u044f \u043f\u043e\u043b\u0435\u0439.\r\n- **\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438**: \u0421\u043e\u0437\u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438, \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0439\u0442\u0435 `model`.\r\n- **Pydantic-\u0441\u0445\u0435\u043c\u044b**: \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432 \u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u043f\u043e\u043b\u044f \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u043b\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438/\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f.\r\n- **\u0422\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438**: \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0439\u0442\u0435 `@connection()` \u0434\u043b\u044f \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0441 \u0434\u043e\u0441\u0442\u0443\u043f\u043e\u043c \u043a \u0431\u0430\u0437\u0435.\r\n- **\u0411\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430**: \u0412\u044b\u043d\u043e\u0441\u0438\u0442\u0435 \u0441\u043b\u043e\u0436\u043d\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0432 \u043c\u043e\u0434\u0443\u043b\u044c `services`.\r\n- **\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435**: \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 `pytest-asyncio` \u0434\u043b\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432, \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u0442\u0435\u0441\u0442\u043e\u0432\u0443\u044e \u0411\u0414 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, SQLite \u0432 \u043f\u0430\u043c\u044f\u0442\u0438).\r\n\r\n## \u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u044f\r\n\r\n- **\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0441\u0442\u044c**: \u0412\u0441\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 `AsyncSession` \u0438 `asyncpg`.\r\n- **\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f**: `HAVING` \u0442\u0440\u0435\u0431\u0443\u0435\u0442 `GROUP BY` (\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 SQL). \u0424\u0438\u043b\u044c\u0442\u0440\u044b \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f (`>`, `<`, \u0438 \u0442.\u0434.).\r\n- **\u041f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c**: \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 SQLAlchemy Core \u0434\u043b\u044f bulk-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439, \u0438\u0437\u0431\u0435\u0433\u0430\u0439\u0442\u0435 \u0441\u043c\u0435\u0448\u0438\u0432\u0430\u043d\u0438\u044f \u0441 ORM \u0434\u043b\u044f \u043f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0449\u0435\u043d\u0438\u044f stale data.\r\n",
"bugtrack_url": null,
"license": "MIT License",
"summary": "Asynchronous database library using SQLAlchemy Core with CRUD operations",
"version": "0.1.3",
"project_urls": {
"Documentation": "https://github.com/tktturik/db-alchemy-core"
},
"split_keywords": [
"database",
" async",
" sqlalchemy",
" postgresql",
" crud",
" alembic",
" pydantic",
" fastapi"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "59b463822d4d24b5d3e7f8bc63829eab292cd691a4ba157098e1929fe205440c",
"md5": "64d4e2c6b67aea066d0d9b37e4cfc0c2",
"sha256": "ef2d17a8cc527bdbdb1a1924a6c20acd040e588330081dfe322604f2ed7e281a"
},
"downloads": -1,
"filename": "dbalchemycore-0.1.3-py3-none-any.whl",
"has_sig": false,
"md5_digest": "64d4e2c6b67aea066d0d9b37e4cfc0c2",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 18331,
"upload_time": "2025-08-31T04:12:00",
"upload_time_iso_8601": "2025-08-31T04:12:00.013894Z",
"url": "https://files.pythonhosted.org/packages/59/b4/63822d4d24b5d3e7f8bc63829eab292cd691a4ba157098e1929fe205440c/dbalchemycore-0.1.3-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "8f82e98525fd8b0396b5011e8be5866a304b145dfabe398a29e8037d08b914b2",
"md5": "63427974f1a19b5794d4b05a84882ac3",
"sha256": "6a42a755784ac5db21e1d5da6ec2042cafda60984d53618cc8e48d921a870405"
},
"downloads": -1,
"filename": "dbalchemycore-0.1.3.tar.gz",
"has_sig": false,
"md5_digest": "63427974f1a19b5794d4b05a84882ac3",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 20817,
"upload_time": "2025-08-31T04:12:01",
"upload_time_iso_8601": "2025-08-31T04:12:01.378433Z",
"url": "https://files.pythonhosted.org/packages/8f/82/e98525fd8b0396b5011e8be5866a304b145dfabe398a29e8037d08b914b2/dbalchemycore-0.1.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-31 04:12:01",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "tktturik",
"github_project": "db-alchemy-core",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "dbalchemycore"
}