dbalchemycore


Namedbalchemycore JSON
Version 0.1.3 PyPI version JSON
download
home_pageNone
SummaryAsynchronous database library using SQLAlchemy Core with CRUD operations
upload_time2025-08-31 04:12:01
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT License
keywords database async sqlalchemy postgresql crud alembic pydantic fastapi
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 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"
}
        
Elapsed time: 1.81679s