Files
2018-lpon-site/_prj_blueprint/DSL.md
T

12 KiB
Raw Blame History

Спецификация: LPON Store Hub DSL (Компактный конструктор интерфейсов на кортежах)

драфт

1. Обзор и цели

Мы реализуем гибкий, управляемый данными (Data-Driven) конструктор страниц внутри бэкенда Django для LPON.RU. Вместо написания отдельных вьюх (Views) и шаблонов под каждый формат или раздел (например, /catalog/cassettes/, /catalog/vinyl/), мы формируем динамические Страницы-Хабы (Hub Pages) с помощью компактного JSON Tuple DSL, который хранится прямо в базе данных.

Система завязана на модель TbArticle. Конфигурация хранится в JSON-поле метаданных статьи (j_article_metadata['hub']). Механизм HUB может использоваться как для чистых хаб-страниц (l_article_type = 'HUB'), так и в качестве опционального блока в любых других статьях/сущностях для подключения динамических витрин и модулей.

Генерализация списков страниц (Отказ от списочных View/Templates)

Все списочные страницы (например, список текстовых материалов /info/, список новостей /blog/, каталоги форматов) рассматриваются как частный случай хаба:

  1. Вместо написания отдельной вьюхи (например, txt_articles_list) в БД создаётся статья со слагом info и типом HUB.
  2. В её DSL-конфигурации прописываются требуемые блоки (например, [["info-articles", "list", 50]]).
  3. Рендеринг выполняют не отдельные функции-представления, а единый универсальный диспетчер хабов (HubView).
  4. Каноничность URL элементов: Внутри шаблонов визуальных блоков ссылки на карточки и статьи выводится строго через href="{{ item.get_absolute_url }}". Модель статьи/товара сама определяет свой канонический адрес по типу (например, /info/privacy-policy или /item/album-slug), исключая путаницу префиксов в ссылках независимо от того, в каком хабе опубликован блок.

2. Синтаксис и структура JSON DSL

Массив hub представляет собой список позиционных кортежей (массивов). Порядок элементов в кортеже фиксирован, при этом все элементы, кроме первого (slug), являются опциональными.

Позиционный порядок параметров: [ "slug", "view_mode", limit, "sort", extra ]

Правила синтаксиса:

{
  "hub": [
    ["source-slug", "view_mode"],
    ["source-slug", "view_mode", limit],
    ["source-slug", "view_mode", limit, "sort_rule"],
    ["source-slug", "view_mode", limit, "sort_rule", {"title": "Кастомный заголовок"}],
    ["!module-slug"]
  ]
}

3. Спецификация блоков

Тип A: Блок подборки товаров или контентной статьи (обычный slug)

Выводит подборку товаров (TbOffer), полученную на основе категории, тега формата или слаг-коллекции, либо встроенную статью/баннер.

Формат: [ "slug", "view_mode", limit, "sort", extra ]

Параметры (позиционные):

  1. slug (str, обязательный): Уникальный слаг TbMusicStyle, TbArticle (Категория/Тег/Статья) или идентификатор формата (например, cassettes-studio, vinyl-rock).
  2. view_mode (str, опциональный, default зависит от ArticleType): Компонент рендеринга (шаблон):
  • carousel → Горизонтальная карусель / слайдер.
  • grid → Стандартная сетка карточек.
  • list → Компактный вертикальный список.
  • teaser → Тизер статьи с кнопкой «Читать далее».
  • full → Полный HTML-контент статьи, встроенный inline.
  • Примечание: Если view_mode не указан, шаблон выбирается автоматически в соответствии с типом сущности (ArticleType модели TbArticle), за которой закреплен слаг. У каждого ArticleType есть свой дефолтный шаблон.
  1. limit (int, опциональный, default=10): Количество выводимых элементов (например, 5, 20).
  2. sort (str, опциональный, default="newest"): Правило сортировки:
  • newest → -t_offer_created (новинки)
  • views → -i_offer_views (самые просматриваемые)
  • favorites → -i_offer_favorites (самые популярные / в избранном)
  • price_asc → f_offer_price (сначала дешевые)
  • price_desc → -f_offer_price (сначала дорогие)
  1. extra (dict, опциональный, default={}): Дополнительные произвольные настройки блока (например, {"title": "Переопределенный заголовок"}).

Примеры:

  • ["cassettes-studio"] — подборка кассет (дефолтные view_mode по ArticleType, limit=10, sort="newest")
  • ["cassettes-studio", "carousel"] — карусель студийных кассет (дефолтные limit=10, sort="newest")
  • ["cassettes-studio", "carousel", 6] — карусель из 6 студийных кассет
  • ["cassettes-studio", "carousel", 6, "views"] — 6 самых просматриваемых кассет в карусели
  • ["cassettes-studio", "carousel", 6, "views", {"title": "Хиты продаж"}] — с переопределенным заголовком

Замечания: view_mode — это название зарегистрированного шаблона в системе. Если view_mode не указан или пуст, вид отображения выбирается автоматически в соответствии с ArticleType найденной сущности. При невозможности определить ArticleType или при незнакомом названии шаблона используется системный фолбэк ("grid").

Полный список доступных видов отображения (view_mode) и поддерживаемых ими шаблонов ведет отдельно (в виде внешней документации компонентов либо будет добавлен ниже в этом документе по мере появления новых визуальных блоков).

Тип B: Динамический модуль / Плагин (Префикс !)

Запускает кастомный Python-модуль, зарегистрированный в системе (например, интерактивные виджеты, спецпредложения корзины, акции).

Формат: [ "!module-slug", extra ]

Отличительный признак: slug начинается с символа ! (восклицательный знак). Так как знак ! недопустим в обычных URL-слагах Django, парсер однозначно и безошибочно определяет, что данный блок является вызовом Python-плагина, а не поиском статьи/категории в БД.

Параметры:

  1. slug (str, обязательный): Уникальный идентификатор с префиксом !, зарегистрированный в PluginRegistry (например, !promo-mandarin, !cart-bonus).
  2. extra (dict, опциональный, default={}): Словарь дополнительных параметров, передаваемых в модуль.

Пример:

  • ["!promo-mandarin"]
  • ["!cart-bonus", {"threshold": 5000}]

4. Полный пример конфига (j_article_metadata)

{
  "hub": [
    ["hero-cassettes-2026", "slider"],
    ["cassettes-studio", "carousel", 6, "views"],
    ["cassettes-studio", "grid", 20, "newest"],
    ["!promo-mandarin"],
    ["cassettes-maxell-used-ud2-c90-ver2", "carousel", 6, "popular"],
    ["cassettes-tdk-ned-cding2-c60", "list", 20, "price_desc"],
    ["guide-how-to-clean-heads", "teaser", 1]
  ]
}

5. Защита и логика бэкенда (Требования к Python / Django)

При разработке парсера DSL:

  1. Защита от зацикливания и бесконечной рекурсии:
  • Передавать множество visited_slugs при рендере страницы.
  • Если вложенный блок пытается загрузить slug, который уже есть в visited_slugs, пропускать блок или выводить отладочный HTML-комментарий (<!-- DSL Error: Circular reference detected for slug -->).
  1. Ограничение глубины вложенности:
  • Жестко ограничить максимальную глубину рекурсии: MAX_RECURSION_DEPTH = 2 (устанавливается в settings.py).
  1. Отказоустойчивость (Graceful Degradation):
  • Если limit передан не числом или sort содержит неизвестную строку, парсер должен тихо применить безопасные дефолты (автовыбор view_mode по ArticleType с фолбэком на 'grid', limit=10, sort='newest'). Ошибка синтаксиса в JSON никогда не должна приводить к 500 Server Error.
  • Если slug (без !) не найден в базе, блок пропускается, а в HTML выводится комментарий: <!-- DSL Error: Slug not found -->.
  • Если модуль !slug не зарегистрирован в PluginRegistry, выводится комментарий: <!-- DSL Error: Module not found -->.
  1. Изоляция данных:
  • Внутри DSL запрещено использовать сырые имена полей БД (например, k_offer_to_item__s_item). Все выборки данных маппятся строго через slug и внутренние обработчики запросов (Query Handlers).
  1. Валидация slug
  • Добавить в валидатор админки статей (TbArticle) проверку на несовпадение slug с существующими роутами сайта из urls.py (чтобы не было конфликта URL-ов).