- 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
13 KiB
trigger
| 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 pippoetry,pipenv,pipx- системные команды вне venv
✅ Разрешено
uv venvsource .venv/bin/activateuv add / uv remove / uv syncuv run <command>
Пример:
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
- легко тестируются
- управляют транзакциями
- Сервис определяет что делать, а не как отдать ответ
Рекомендуемый паттерн:
class EntityService:
@classmethod
def do_something(cls, *, data):
...
4.4 Модели (ORM)
Модели должны быть:
- простыми
- декларативными
Допускается:
__str__- простые computed properties
- минимальные helper-методы
❌ Запрещено:
- бизнес-логика
- workflow
- сигналы как логика
- условия, зависящие от сценариев
👉 Любые исключения — ТОЛЬКО после обсуждения в чате.
5) Celery
- Task = thin wrapper
- Task вызывает сервис, а не содержит логику
- Таски:
- идемпотентны
- логируют начало и завершение
- Ретраи:
- только для временных ошибок
- с backoff
6) База данных и миграции
- Любое изменение моделей → миграции обязательны
- Миграции:
- детерминированные
- без ручной магии без причины
Проверка перед коммитом:
uv run python manage.py makemigrations --check --dry-run
PostgreSQL:
- транзакции использовать осознанно
select_for_update()при гонках- Raw SQL — только с объяснением
7) Тестирование
- Любая бизнес-логика → тесты
- В первую очередь тестируется сервисный слой
- API — happy path + edge cases
Запуск:
uv run python manage.py test
8) pre-commit (обязателен)
- Любой код обязан проходить pre-commit
- Агент обязан учитывать проверки форматирования и линтинга
pre-commit run --all-files
9) CI (Gitea Actions)
- Используется Gitea Actions
- ❌ GitHub Actions запрещены
- Любые изменения:
- не должны ломать CI
- Если меняются:
- зависимости
- команды тестов
- миграции
→ агент обязан указать необходимость правок workflow
10) Apache + mod_wsgi
- Используется ТОЛЬКО WSGI
- ASGI запрещён без отдельного обсуждения
- Любые изменения в
wsgi.py, путях, статике:- сопровождаются пояснением
- требуют перезапуска Apache
systemctl restart apache2
Учитывать ограничения и права Astra Linux.
11) Работа с репозиторием
- Минимальный diff — приоритет
- ❌ Не коммитить:
.venv- артефакты
- дампы БД
- Массовый рефакторинг — только по явному запросу
12) Anti-patterns (ЗАПРЕЩЕНО)
- Fat Models
- God Views
- Бизнес-логика в Serializers
- Сигналы как workflow
- Магия в
save() - Прямые импорты моделей между apps
- Сложная логика в queryset как бизнес-правило
13) Формат ответа ИИ-агента (ОБЯЗАТЕЛЬНЫЙ)
Каждый ответ должен содержать:
- Что меняем
- Файлы / патч
- Команды (через uv)
- Проверки (tests / pre-commit / CI)
- Риски / замечания
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
Порядок действий:
- Проверить
apps.coreна наличие нужного базового класса/миксина - Наследоваться от существующего, а не создавать с нуля
- Если нужного нет — обсудить добавление в 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 |
Пример правильного использования:
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']
Порядок наследования миксинов:
UUIDPrimaryKeyMixin(если нужен)TimestampMixinSoftDeleteMixin(если нужен)AuditMixin(если нужен)OrderableMixin/StatusMixin/SlugMixinmodels.Model(последним)
15.2 Management Commands
Все management commands наследуются от BaseAppCommand:
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:
# В сервисе при запуске задачи
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:
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")для русских данных