Files
Aleksandr Meshchriakov f121445313 feat(core): add core module with mixins, services, and background jobs
- Add Model Mixins: TimestampMixin, SoftDeleteMixin, AuditMixin, etc.
- Add Base Services: BaseService, BulkOperationsMixin, QueryOptimizerMixin
- Add Base ViewSets with bulk operations
- Add BackgroundJob model for Celery task tracking
- Add BaseAppCommand for management commands
- Add permissions, pagination, filters, cache, logging
- Migrate tests to factory_boy + faker
- Add CHANGELOG.md
- 297 tests passing
2026-01-21 11:47:26 +01:00

423 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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")` для русских данных