feat: updated CLAUDE.md

This commit is contained in:
Artem Tsyrulnikov
2025-12-23 10:43:01 +03:00
parent 829cd82bcf
commit c6d3c78e0c

View File

@@ -7,9 +7,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
This is a FastAPI + Telegram Bot backend application for managing Telegram channel integrations. The project uses:
- **FastAPI** for HTTP API endpoints
- **aiogram** for Telegram bot functionality
- **SQLAlchemy 2.0** (async) for database operations
- **Tortoise ORM** (async) for database operations
- **PostgreSQL** with asyncpg driver
- **Alembic** for database migrations
- **aerich** for database migrations
- **uv** for Python package management
## Development Commands
@@ -23,7 +23,7 @@ uv sync
docker-compose up -d
# Run database migrations
uv run alembic upgrade head
make migrate-up # or: export $(cat .env | xargs) && aerich upgrade
```
### Running the Application
@@ -58,14 +58,19 @@ uv run pytest tests/test_file.py::test_function_name
### Database Migrations
```bash
# Create a new migration
uv run alembic revision --autogenerate -m "description"
# Create a new migration (auto-generated based on model changes)
make migrate-create # or: export $(cat .env | xargs) && aerich migrate
# Apply migrations
uv run alembic upgrade head
make migrate-up # or: export $(cat .env | xargs) && aerich upgrade
# Rollback one migration
uv run alembic downgrade -1
make migrate-down # or: export $(cat .env | xargs) && aerich downgrade
# NOTE: For custom constraints (CHECK, custom indexes, etc.) that cannot be defined
# in Tortoise ORM models, create empty migrations and edit them manually:
export $(cat .env | xargs) && aerich migrate --name "add_check_constraint" --empty
# Then edit the created file and add `# ruff: noqa` and `# mypy: ignore-errors` at the top.
```
## Architecture
@@ -75,12 +80,12 @@ uv run alembic downgrade -1
The codebase follows a clean architecture pattern with clear separation of concerns:
1. **Domain Layer** (`src/domain/`)
- SQLAlchemy ORM models that represent database entities
- All models inherit from `domain.Base` which provides:
- Tortoise ORM models that represent database entities
- All models inherit from `domain.base.TimestampedModel` which provides:
- Auto-generated `id` (UUID primary key)
- Timestamps: `created_at`, `updated_at`, `deleted_at`
- Automatic table naming (pluralized lowercase class name)
- Timezone-aware datetime fields
- Use `class Meta: table = 'table_name'` for explicit table naming
2. **Use Case Layer** (`src/usecase/`)
- Business logic functions organized by feature
@@ -322,17 +327,55 @@ await self.telegram_bot.send_message_with_inline_keyboard('Message', chat_id, bu
**Типизация Enum в Domain:**
- Используйте `enum.StrEnum` для строковых enum (Python 3.11+)
- Enum значения в lowercase с underscores
- Передавайте enum в SQLAlchemy через `Enum(EnumClass)`
- Используйте `fields.CharEnumField()` в Tortoise ORM
```python
import enum
from sqlalchemy import Enum
from sqlalchemy.orm import Mapped, mapped_column
from tortoise import fields
from .base import TimestampedModel
class TelegramStateEnum(enum.StrEnum):
CREATIVE_WAITING_CHANNEL = 'creative_waiting_channel'
CREATIVE_WAITING_NAME = 'creative_waiting_name'
class TelegramState(Base):
state: Mapped[TelegramStateEnum] = mapped_column(Enum(TelegramStateEnum), nullable=False)
class TelegramState(TimestampedModel):
state = fields.CharEnumField(TelegramStateEnum, max_length=26)
class Meta:
table = 'telegram_state'
```
**Database Constraints:**
- Foreign keys: Use `fields.ForeignKeyField()` with `on_delete=fields.CASCADE`
- Unique constraints: Use `unique=True` or `unique_together` in Meta
- CHECK constraints: Cannot be defined in Tortoise models - create manual migrations
- Indexes: Use `index=True` on fields or define in Meta
Example with CHECK constraint:
```bash
# Create empty migration
export $(cat .env | xargs) && aerich migrate --name "add_check_constraint" --empty
```
Then edit the created file:
```python
# migrations/models/1_xxx_add_check_constraint.py
# ruff: noqa
# mypy: ignore-errors
from tortoise import BaseDBAsyncClient
RUN_IN_TRANSACTION = True
async def upgrade(db: BaseDBAsyncClient) -> str:
return """
ALTER TABLE "table_name"
ADD CONSTRAINT "constraint_name"
CHECK (your_condition);
"""
async def downgrade(db: BaseDBAsyncClient) -> str:
return """
ALTER TABLE "table_name"
DROP CONSTRAINT IF EXISTS "constraint_name";
"""
```