423 lines
13 KiB
Markdown
423 lines
13 KiB
Markdown
---
|
||
trigger: always_on
|
||
---
|
||
|
||
## 0) Язык и стиль общения (СТРОГО)
|
||
- ИИ-агент **ВСЕГДА отвечает на русском языке**
|
||
- Английский допускается ТОЛЬКО для:
|
||
- имён библиотек
|
||
- имён классов, функций, переменных
|
||
- CLI-команд
|
||
- Тон: инженерный, практичный, без воды и маркетинга
|
||
|
||
---
|
||
|
||
## 1) Базовые принципы (НЕ ОБСУЖДАЮТСЯ)
|
||
Проект разрабатывается строго по принципам:
|
||
|
||
- **SOLID**
|
||
- **KISS**
|
||
- **DRY**
|
||
|
||
Правила приоритета:
|
||
- красиво vs просто → **простота**
|
||
- умно vs поддерживаемо → **поддерживаемость**
|
||
- магия vs явность → **явность**
|
||
|
||
---
|
||
|
||
## 2) Контекст проекта
|
||
- ОС: **Astra Linux 1.8**
|
||
- Python: **3.11.2**
|
||
- Django: **3.x (указано 3.14)**
|
||
- Django REST Framework (DRF)
|
||
- Celery
|
||
- PostgreSQL **15.10**
|
||
- Apache **2.4.57**
|
||
- mod_wsgi **4.9.4**
|
||
|
||
### Инструменты разработки
|
||
- **uv**
|
||
- **виртуальная среда**
|
||
- **pre-commit**
|
||
- **Gitea Actions (CI)**
|
||
- Тесты: `django test`
|
||
- Линтинг: `ruff`
|
||
- **ЗАПРЕЩЕНО** создавать тестовые скрипты для демонстрации, все должно быть исправлено в рамках проекта
|
||
|
||
---
|
||
|
||
## 3) Окружение и команды (СТРОГО)
|
||
Все команды:
|
||
- выполняются **только внутри виртуальной среды**
|
||
- используют **uv**
|
||
- считаются выполняемыми из корня проекта
|
||
|
||
### ❌ Запрещено
|
||
- `pip`, `python -m pip`
|
||
- `poetry`, `pipenv`, `pipx`
|
||
- системные команды вне venv
|
||
|
||
### ✅ Разрешено
|
||
- `uv venv`
|
||
- `source .venv/bin/activate`
|
||
- `uv add / uv remove / uv sync`
|
||
- `uv run <command>`
|
||
|
||
Пример:
|
||
```bash
|
||
uv run python manage.py test
|
||
```
|
||
|
||
---
|
||
|
||
## 4) Архитектура и слои ответственности (КРИТИЧНО)
|
||
|
||
### 4.1 View (DRF)
|
||
View отвечает ТОЛЬКО за:
|
||
- приём HTTP-запроса
|
||
- проверку прав доступа
|
||
- работу с serializer
|
||
- вызов сервисного слоя
|
||
|
||
❌ Запрещено:
|
||
- бизнес-логика
|
||
- сложные условия
|
||
- транзакции
|
||
- сложная работа с ORM
|
||
|
||
---
|
||
|
||
### 4.2 Serializer
|
||
Serializer отвечает за:
|
||
- валидацию данных
|
||
- преобразование вход/выход
|
||
|
||
Допускается:
|
||
- field-level validation
|
||
- object-level validation
|
||
|
||
❌ Запрещено:
|
||
- бизнес-правила
|
||
- side-effects
|
||
- сложная логика в `save()`
|
||
|
||
---
|
||
|
||
### 4.3 Сервисный слой (Business Logic)
|
||
- **ВСЯ бизнес-логика живёт здесь**
|
||
- Сервисы:
|
||
- не зависят от HTTP
|
||
- легко тестируются
|
||
- управляют транзакциями
|
||
- Сервис определяет *что* делать, а не *как* отдать ответ
|
||
|
||
Рекомендуемый паттерн:
|
||
```python
|
||
class EntityService:
|
||
@classmethod
|
||
def do_something(cls, *, data):
|
||
...
|
||
```
|
||
|
||
---
|
||
|
||
### 4.4 Модели (ORM)
|
||
Модели должны быть:
|
||
- простыми
|
||
- декларативными
|
||
|
||
Допускается:
|
||
- `__str__`
|
||
- простые computed properties
|
||
- минимальные helper-методы
|
||
|
||
❌ Запрещено:
|
||
- бизнес-логика
|
||
- workflow
|
||
- сигналы как логика
|
||
- условия, зависящие от сценариев
|
||
|
||
👉 **Любые исключения — ТОЛЬКО после обсуждения в чате.**
|
||
|
||
---
|
||
|
||
## 5) Celery
|
||
- Task = **thin wrapper**
|
||
- Task вызывает сервис, а не содержит логику
|
||
- Таски:
|
||
- идемпотентны
|
||
- логируют начало и завершение
|
||
- Ретраи:
|
||
- только для временных ошибок
|
||
- с backoff
|
||
|
||
---
|
||
|
||
## 6) База данных и миграции
|
||
- Любое изменение моделей → миграции обязательны
|
||
- Миграции:
|
||
- детерминированные
|
||
- без ручной магии без причины
|
||
|
||
Проверка перед коммитом:
|
||
```bash
|
||
uv run python manage.py makemigrations --check --dry-run
|
||
```
|
||
|
||
PostgreSQL:
|
||
- транзакции использовать осознанно
|
||
- `select_for_update()` при гонках
|
||
- Raw SQL — только с объяснением
|
||
|
||
---
|
||
|
||
## 7) Тестирование
|
||
- Любая бизнес-логика → тесты
|
||
- В первую очередь тестируется сервисный слой
|
||
- API — happy path + edge cases
|
||
|
||
Запуск:
|
||
```bash
|
||
uv run python manage.py test
|
||
```
|
||
|
||
---
|
||
|
||
## 8) pre-commit (обязателен)
|
||
- Любой код обязан проходить pre-commit
|
||
- Агент обязан учитывать проверки форматирования и линтинга
|
||
|
||
```bash
|
||
pre-commit run --all-files
|
||
```
|
||
|
||
---
|
||
|
||
## 9) CI (Gitea Actions)
|
||
- Используется **Gitea Actions**
|
||
- ❌ GitHub Actions запрещены
|
||
- Любые изменения:
|
||
- не должны ломать CI
|
||
- Если меняются:
|
||
- зависимости
|
||
- команды тестов
|
||
- миграции
|
||
→ агент обязан указать необходимость правок workflow
|
||
|
||
---
|
||
|
||
## 10) Apache + mod_wsgi
|
||
- Используется **ТОЛЬКО WSGI**
|
||
- ASGI запрещён без отдельного обсуждения
|
||
- Любые изменения в `wsgi.py`, путях, статике:
|
||
- сопровождаются пояснением
|
||
- требуют перезапуска Apache
|
||
|
||
```bash
|
||
systemctl restart apache2
|
||
```
|
||
|
||
Учитывать ограничения и права Astra Linux.
|
||
|
||
---
|
||
|
||
## 11) Работа с репозиторием
|
||
- Минимальный diff — приоритет
|
||
- ❌ Не коммитить:
|
||
- `.venv`
|
||
- артефакты
|
||
- дампы БД
|
||
- Массовый рефакторинг — только по явному запросу
|
||
|
||
---
|
||
|
||
## 12) Anti-patterns (ЗАПРЕЩЕНО)
|
||
- Fat Models
|
||
- God Views
|
||
- Бизнес-логика в Serializers
|
||
- Сигналы как workflow
|
||
- Магия в `save()`
|
||
- Прямые импорты моделей между apps
|
||
- Сложная логика в queryset как бизнес-правило
|
||
|
||
---
|
||
|
||
## 13) Формат ответа ИИ-агента (ОБЯЗАТЕЛЬНЫЙ)
|
||
Каждый ответ должен содержать:
|
||
|
||
1. **Что меняем**
|
||
2. **Файлы / патч**
|
||
3. **Команды (через uv)**
|
||
4. **Проверки (tests / pre-commit / CI)**
|
||
5. **Риски / замечания**
|
||
|
||
---
|
||
|
||
## 14) Исключения
|
||
- ИИ-агент **НЕ внедряет исключения сам**
|
||
- Агент:
|
||
- описывает стандартное решение
|
||
- объясняет, почему оно не подходит
|
||
- запрашивает разрешение в чате
|
||
|
||
## 15) Структура проекта и миксины (ОБЯЗАТЕЛЬНО)
|
||
|
||
### 15.0 Правило Core-First (КРИТИЧНО)
|
||
**ПЕРЕД созданием любого нового компонента** агент ОБЯЗАН проверить модуль `apps.core`:
|
||
|
||
```
|
||
src/apps/core/
|
||
├── mixins.py # Model mixins (TimestampMixin, SoftDeleteMixin, etc.)
|
||
├── services.py # BaseService, BackgroundJobService
|
||
├── views.py # Health checks, BackgroundJob API
|
||
├── viewsets.py # BaseViewSet, ReadOnlyViewSet
|
||
├── exceptions.py # APIError, NotFoundError, ValidationError
|
||
├── permissions.py # IsOwner, IsAdminOrReadOnly, etc.
|
||
├── pagination.py # CursorPagination
|
||
├── filters.py # BaseFilterSet
|
||
├── cache.py # cache_result, invalidate_cache
|
||
├── tasks.py # BaseTask для Celery
|
||
├── logging.py # StructuredLogger
|
||
├── middleware.py # RequestIDMiddleware
|
||
├── signals.py # SignalDispatcher
|
||
├── responses.py # APIResponse wrapper
|
||
├── openapi.py # api_docs decorator
|
||
└── management/commands/base.py # BaseAppCommand
|
||
```
|
||
|
||
**Порядок действий:**
|
||
1. Проверить `apps.core` на наличие нужного базового класса/миксина
|
||
2. Наследоваться от существующего, а не создавать с нуля
|
||
3. Если нужного нет — обсудить добавление в core
|
||
|
||
❌ **ЗАПРЕЩЕНО:** создавать дублирующую функциональность в app-модулях
|
||
|
||
---
|
||
|
||
### 15.1 Model Mixins
|
||
При создании моделей **ОБЯЗАТЕЛЬНО** использовать миксины из `apps.core.mixins`:
|
||
|
||
| Миксин | Когда использовать | Поля |
|
||
|--------|-------------------|------|
|
||
| `TimestampMixin` | **ВСЕГДА** для любой модели | `created_at`, `updated_at` |
|
||
| `UUIDPrimaryKeyMixin` | Когда нужен UUID вместо int ID | `id` (UUID) |
|
||
| `SoftDeleteMixin` | Когда нельзя физически удалять | `is_deleted`, `deleted_at` |
|
||
| `AuditMixin` | Когда нужно знать кто создал/изменил | `created_by`, `updated_by` |
|
||
| `OrderableMixin` | Для сортируемых списков | `order` |
|
||
| `StatusMixin` | Для моделей со статусами | `status` |
|
||
| `SlugMixin` | Для URL-friendly идентификаторов | `slug` |
|
||
|
||
**Пример правильного использования:**
|
||
```python
|
||
from apps.core.mixins import TimestampMixin, SoftDeleteMixin, AuditMixin
|
||
|
||
class Document(TimestampMixin, SoftDeleteMixin, AuditMixin, models.Model):
|
||
"""Документ с историей и мягким удалением."""
|
||
title = models.CharField(max_length=200)
|
||
|
||
class Meta:
|
||
ordering = ['-created_at']
|
||
```
|
||
|
||
**Порядок наследования миксинов:**
|
||
1. `UUIDPrimaryKeyMixin` (если нужен)
|
||
2. `TimestampMixin`
|
||
3. `SoftDeleteMixin` (если нужен)
|
||
4. `AuditMixin` (если нужен)
|
||
5. `OrderableMixin` / `StatusMixin` / `SlugMixin`
|
||
6. `models.Model` (последним)
|
||
|
||
---
|
||
|
||
### 15.2 Management Commands
|
||
Все management commands наследуются от `BaseAppCommand`:
|
||
|
||
```python
|
||
from apps.core.management.commands.base import BaseAppCommand
|
||
|
||
class Command(BaseAppCommand):
|
||
help = 'Описание команды'
|
||
use_transaction = True # Обернуть в транзакцию
|
||
|
||
def add_arguments(self, parser):
|
||
super().add_arguments(parser) # Добавляет --dry-run, --silent
|
||
parser.add_argument('--my-arg', type=str)
|
||
|
||
def execute_command(self, *args, **options):
|
||
items = MyModel.objects.all()
|
||
|
||
for item in self.progress_iter(items, desc="Обработка"):
|
||
if not self.dry_run:
|
||
self.process(item)
|
||
|
||
return "Обработано успешно"
|
||
```
|
||
|
||
**Возможности BaseAppCommand:**
|
||
- `--dry-run` — тестовый запуск без изменений
|
||
- `--silent` — минимальный вывод
|
||
- `self.progress_iter()` — прогресс-бар
|
||
- `self.timed_operation()` — измерение времени
|
||
- `self.confirm()` — подтверждение
|
||
- `self.log_info/success/warning/error()` — логирование
|
||
|
||
---
|
||
|
||
### 15.3 Background Jobs (Celery)
|
||
Для отслеживания статуса фоновых задач использовать `BackgroundJob`:
|
||
|
||
```python
|
||
# В сервисе при запуске задачи
|
||
from apps.core.services import BackgroundJobService
|
||
|
||
job = BackgroundJobService.create_job(
|
||
task_id=task.id,
|
||
task_name="apps.myapp.tasks.process_data",
|
||
user_id=request.user.id,
|
||
)
|
||
|
||
# В Celery таске
|
||
from apps.core.models import BackgroundJob
|
||
|
||
@shared_task(bind=True)
|
||
def my_task(self, data):
|
||
job = BackgroundJob.objects.get(task_id=self.request.id)
|
||
job.mark_started()
|
||
|
||
for i, item in enumerate(items):
|
||
process(item)
|
||
job.update_progress(i * 100 // len(items), "Обработка...")
|
||
|
||
job.complete(result={"processed": len(items)})
|
||
```
|
||
|
||
**API эндпоинты:**
|
||
- `GET /api/v1/jobs/` — список задач пользователя
|
||
- `GET /api/v1/jobs/{task_id}/` — статус конкретной задачи
|
||
|
||
---
|
||
|
||
### 15.4 Factories (тестирование)
|
||
Все фабрики используют `factory_boy` + `faker`:
|
||
|
||
```python
|
||
import factory
|
||
from faker import Faker
|
||
|
||
fake = Faker("ru_RU")
|
||
|
||
class MyModelFactory(factory.django.DjangoModelFactory):
|
||
class Meta:
|
||
model = MyModel
|
||
|
||
name = factory.LazyAttribute(lambda _: fake.word())
|
||
email = factory.LazyAttribute(lambda _: fake.unique.email())
|
||
```
|
||
|
||
**Правила:**
|
||
- Никакого хардкода в тестах (`"test@example.com"` → `fake.email()`)
|
||
- Использовать `fake.unique.*` для уникальных полей
|
||
- Локаль: `Faker("ru_RU")` для русских данных
|
||
|