# Как это работает

Основная идея: **интроспекция argparse → динамическая генерация форм → выполнение команды**.

## Поток данных

```mermaid
graph LR
    A[discover_commands] --> B[introspect_command]
    B --> C[build_command_form]
    C --> D[execute_command]
    D --> E[CommandResult]
```

## Пошагово

### 1. Обнаружение

`discover_commands()` находит все зарегистрированные management-команды через `django.core.management.get_commands()` и возвращает список датаклассов `CommandInfo`.

### 2. Интроспекция

`introspect_command()` загружает `ArgumentParser` команды и разбирает его `_actions` в датаклассы `ArgumentInfo`. Каждый `ArgumentInfo` содержит имя аргумента, тип, текст справки, значение по умолчанию, варианты выбора и флаг обязательности.

Результаты кешируются через `@lru_cache` для производительности.

### 3. Генерация формы

`build_command_form()` преобразует каждый `ArgumentInfo` в поле Django-формы:

| Тип аргумента | Поле формы |
|---|---|
| `store_true` / `store_false` | `BooleanField` (чекбокс) |
| `choices` | `ChoiceField` (выпадающий список) |
| `type=int` | `IntegerField` |
| `type=float` | `FloatField` |
| `type=Path` | `PathField` (загрузка файла + переключение на текст) |
| по умолчанию / `type=str` | `CharField` |

Аргументы отображаются в двух группах:

- **Аргументы команды** — показаны на видном месте
- **Опции BaseCommand** (`--verbosity`, `--traceback` и т.д.) — в сворачиваемой секции

### 4. Выполнение

`execute_command()` запускает команду через Django `call_command()`, перехватывая stdout и stderr. Измеряет время выполнения и возвращает датакласс `CommandResult`.

Команды выполняются с настраиваемым таймаутом (по умолчанию 30 сек.) в отдельном потоке. Большие выводы автоматически обрезаются до первых + последних строк. Подробности в разделе [Конфигурация](configuration.md).

### 5. Отображение результата

Страница результата показывает:

- Имя команды и использованные аргументы
- Вывод stdout
- Вывод stderr (если есть)
- Длительность выполнения
- Статус успеха/ошибки с соответствующим оформлением

## Работа с Path-аргументами

Если management-команда использует `type=pathlib.Path` для любого аргумента, форма отображает специальный виджет **PathField** с двумя режимами ввода:

### Загрузка файла (по умолчанию)

Пользователь выбирает файл со своего компьютера. Файл:

1. Записывается по частям (chunks) во временный файл в `/tmp/` (безопасно для больших файлов)
2. Путь к временному файлу передаётся как строковый аргумент в `call_command()`
3. После выполнения команды (успех или ошибка) временный файл **всегда удаляется**

Это основной режим — удобен, когда команда ожидает файл с данными (CSV-импорт, загрузка фикстур и т.д.).

### Ввод пути строкой

Нажмите «или введите путь как текст», чтобы переключиться на текстовое поле. Пользователь вводит путь к файлу на сервере напрямую (например, `/data/exports/report.csv`). Файл не загружается и не создаётся — строка пути передаётся команде как есть.

Полезно, когда файл уже существует на сервере или когда аргумент — это путь для выходного файла, куда команда запишет результат.

### Как это работает внутри

```
PathField (форма) → UploadedFile или str
    ↓
_temporary_uploaded_files (контекстный менеджер)
    ↓ если UploadedFile:
    сохранить в /tmp/<уникальное-имя> через chunks()
    заменить значение формы на строку пути
    ↓
call_command(..., path="/tmp/abc123")
    ↓
finally: удалить временный файл
```

### Определение Path-аргументов в команде

```python
# myapp/management/commands/import_data.py
import pathlib
from django.core.management.base import BaseCommand

class Command(BaseCommand):
    def add_arguments(self, parser):
        parser.add_argument('input_file', type=pathlib.Path, help='CSV-файл для импорта')
        parser.add_argument('--config', type=pathlib.Path, required=False, help='Файл конфигурации')
```

Автоматически определяются как `pathlib.Path`, так и подклассы `pathlib.PurePath`.

## Ключевые датаклассы

- **`CommandInfo`** — имя команды, метка приложения, текст справки
- **`ArgumentInfo`** — имя аргумента, тип, значение по умолчанию, варианты выбора, справка, флаг обязательности
- **`CommandResult`** — stdout, stderr, код возврата, длительность

## Архитектурные заметки

- **Без моделей БД** — всё извлекается из argparse во время выполнения
- **Без миграций** — нечего мигрировать
- **Кеширование** — результаты интроспекции кешируются через `@lru_cache`
- **Только суперпользователи** — все представления требуют `is_superuser`
- **Список разрешённых команд** — видимые команды можно ограничить через `MANAGEMENT_UI["COMMANDS"]`
- **Таймаут выполнения** — команды завершаются при превышении настраиваемого таймаута (по умолчанию 30 сек.)
- **Обрезка вывода** — большие выводы обрезаются для предотвращения перегрузки браузера
