# Спецификация: LPON Store Hub DSL (Компактный конструктор интерфейсов на кортежах) драфт ## 1. Обзор и цели Мы реализуем гибкий, управленный данными (Data-Driven) конструктор страниц внутри бэкенда Django для **LPON.RU**. Вместо написания отдельных вьюх (Views) и шаблонов под каждый формат или раздел (например, `/catalog/cassettes/`, `/catalog/vinyl/`), мы формируем динамические **Страницы-Хабы (Hub Pages)** с помощью компактного JSON Tuple DSL, который хранится прямо в базе данных. Система завязана на модель `TbArticle` с типом `l_article_type = 'HUB'`. Конфигурация хранится в JSON-поле метаданных статьи (`j_article_metadata['hub']`). --- ## 2. Синтаксис и структура JSON DSL Массив `hub` представляет собой список позиционных кортежей (массивов). Движок определяет тип блока на основе длины массива и типов переданных значений. ### Правила синтаксиса: ```json { "hub": [ ["source-slug", "view_mode"], ["source-slug", limit, "sort_rule", "view_mode"], ["module-or-article-slug"] ] } ``` ## 3. Спецификация блоков ### Тип A: Блок предложений товаров (по умолчанию Длина = 4) Выводит подборку товаров (TbOffer), полученную на основе категории, тега формата или слаг-коллекции. Формат: `[ "slug", limit, "sort", "view_mode" ]` Параметры: 1. `slug` (str): Уникальный слаг TbMusicStyle, TbArticle (Категория/Тег) или идентификатор формата (например, cassettes-studio, cassettes-used-blank). 2. `limit` (int): Количество выводимых товаров (например, 5, 20). Фолбэк при ошибке: 10. 3. `sort` (str): Правило сортировки: - `views` → -i_offer_views (самые просматриваемые) - `favorites` → -i_offer_favorites (самые популярные / в избранном) - `newest` → -t_offer_created (новинки) - `price_asc` → f_offer_price (сначала дешевые) - `price_desc` → -f_offer_price (сначала дорогие) 4. `view_mode` (str): Компонент рендеринга (шаблон): - `carousel` → Горизонтальная карусель / слайдер. - `grid` → Стандартная сетка карточек. - `list` → Компактный вертикальный список. Пример: ["cassettes-studio", 5, "views", "carousel"] Замечания: `view_mode` -- это название зарегистрированного шаблона в системе. Если передан неизвестный `view_mode` или он отсутствует, используется дефолтный `list`. ### Тип B: Баннер / Вложенная статья / Вложенный хаб (Длина = 2) Встраивает существующую статью, рекламный баннер или массив вложенного хаба. Формат: [ "slug", "view_mode" ] Параметры: 1. `slug` (str): Слаг целевой TbArticle. 2. `view_mode` (str): Режим отображения: - `slider` / `hero` → Главный баннер / промо-слайдер. - `teaser` → Тизер статьи с кнопкой «Читать далее». - `full` → Полный HTML-контент статьи, встроенный inline. Пример: ["hero-banner-cassettes", "slider"] ### Тип C: Динамический модуль / Плагин (Длина = 1) Запускает кастомный Python-модуль, зарегистрированный в системе (например, расчет скидок, интерактивные виджеты, реклама). Формат: [ "module-slug" ] Параметры: 1. `slug` (str): Уникальный идентификатор, зарегистрированный в PluginRegistry (например, module-promo-mandarin, module-ad-banner-1). Пример: ["module-promo-mandarin"] ## 4. Полный пример конфига (j_article_metadata) { "hub": [ ["hero-cassettes-2026", "slider"], ["cassettes-studio", 6, "views", "carousel"], ["cassettes-studio", 20, "newest", "grid"], ["module-mandarin-promo"], ["cassettes-maxell-used-ud2-c90-ver2", 6, "popular", "carousel"], ["cassettes-tdk-ned-cding2-c60", 20, "price_desc", "list"], ["guide-how-to-clean-heads", "teaser"] ] } ## 5. Защита и логика бэкенда (Требования к Python / Django) При разработке парсера DSL: 1. Защита от зацикливания и бесконечной рекурсии: - Передавать множество `visited_slugs` при рендере страницы. - Если вложенный блок пытается загрузить `slug`, который уже есть в `visited_slugs`, пропускать блок или выводить отладочный HTML-комментарий (``). 2. Ограничение глубины вложенности: - Жестко ограничить максимальную глубину рекурсии: `MAX_RECURSION_DEPTH = 2` (устанавливается в `settings.py`). 3. Отказоустойчивость (Graceful Degradation): - Если limit передан не числом или sort содержит неизвестную строку, парсер должен тихо применить безопасные дефолты (`limit=10`, `sort='newest'`, `view_mode='grid'`). Ошибка синтаксиса в JSON никогда не должна приводить к `500 Server Error`. - Если `slug` не найден в базе, блок пропускается, а в HTML выводится комментарий: ``. 4. Изоляция данных: - Внутри DSL запрещено использовать сырые имена полей БД (например, k_offer_to_item__s_item). Все выборки данных маппятся строго через `slug` и внутренние обработчики запросов (Query Handlers). - Все ключи из json['hub'] должны быть объявлены заранее. 5. Валидация slug - Нужно добавить в валидатор админки статей (TbArticle) проверку на "не совпадение" slug с существующими роутами сайта из `'urls.py'` (чтобы не было конфликта URL-ов).