Files
mostovik-backend/.qoder/rules/main.md
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

13 KiB
Raw Blame History

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 pip
  • poetry, pipenv, pipx
  • системные команды вне venv

Разрешено

  • uv venv
  • source .venv/bin/activate
  • uv add / uv remove / uv sync
  • uv 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) Формат ответа ИИ-агента (ОБЯЗАТЕЛЬНЫЙ)

Каждый ответ должен содержать:

  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

Пример правильного использования:

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:

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") для русских данных