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

17 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 / source), являются опциональными и имеют безопасные значения по умолчанию.

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

Описание позиций кортежа:

  1. slug / source (str, 1-я позиция, обязательный):

    • Слаг точечной статьи/категории (например, "cassettes-studio").
    • Динамическая выборка с двоеточием (например, "offers:artist:the-beatles", "articles:type:info").
    • Модуль/плагин с префиксом ! (например, "!promo-mandarin").
  2. view_mode (str, 2-я позиция, опциональный, default зависит от ArticleType или "grid"):

    • Название компонента рендеринга (шаблона): carousel, grid, list, teaser, full, slider.
    • Поддерживает составной синтаксис через + (например, "teaser+list").
  3. limit (int | str, 3-я позиция, опциональный, default=10):

    • Количество выбираемых элементов (например, 10, 50).
    • Для составных шаблонов задаётся через + (например, "2+15").
  4. sort (str, 4-я позиция, опциональный, default="new+"):

    • Правило сортировки элементов выборки (см. правила ниже: new+, price+, price-, random и т.д.).
  5. extra (dict, 5-я позиция, опциональный, default={}):

    • Произвольный словарь с дополнительными настройками блока (например, {"title": "Кастомный заголовок"}).

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

{
  "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": "Кастомный заголовок"}],
    ["articles:type:info", "teaser+list", "2+15", "new+"],
    ["!module-slug"]
  ]
}

3. Спецификация блоков и Мета-язык

Специальные символы синтаксиса DSL

Символ Назначение Пример Описание
! Модуль / Плагин "!promo-mandarin", "!cart-bonus" Запуск зарегистрированного Python-модуля
: Динамическая выборка (Namespace) "articles:type:info", "offers:artist:beatles" Выборка из БД (офферы или статьи) по критерию
+ Составной режим отображения и лимита "teaser+list", "2+15" Один SQL-запрос с разбиением результатов на разные шаблоны

Правила сортировки (sort)

Для параметра sort (4-я позиция) используются короткие наглядные обозначения направления сортировки (+ — по убыванию/свежие/популярные, - — по возрастанию/старые/дешевые):

Значение sort Смысл сортировки Эквивалент поля в БД
new+ (default) Сначала новые (новинки) -t_created / -t_article_created
new- Сначала старые t_created / t_article_created
price+ Сначала дешевые f_offer_price (по возрастанию)
price- Сначала дорогие -f_offer_price (по убыванию)
view+ Самые просматриваемые (хиты) -i_views / -i_article_views
view- Наименее просматриваемые i_views / i_article_views
favorite+ Часто в избранном -i_favorites / -i_article_favorites
favorite- Редко в избранном i_favorites / i_article_favorites
random Случайная выборка ? (order_by('?'))

Примечание: Для обратной совместимости и отказоустойчивости парсер бэкенда также принимает устаревшие текстовые значения (newest \rightarrow new+, popular \rightarrow view+, price_asc \rightarrow price+, price_desc \rightarrow price-).


Тип A: Конкретная точечная сущность (Обычный slug)

Загружает конкретную статью, категорию или жанр по их собственному первичному слагу в БД.

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

Примеры:

  • ["cassettes-studio"] — конкретная категория/статья (дефолтные view_mode по ArticleType, limit=10, sort="new+")
  • ["guide-how-to-clean-heads", "teaser"] — тизер конкретной статьи

Тип B: Динамическая выборка / Запрос к БД (Префикс с двоеточием :)

Позволяет формировать динамические списки офферов (offers:...) или статей (articles:...) по заданным критериям без создания отдельных вьюх.

Формат источника (source):

  • offers:artist:<slug> — офферы конкретного исполнителя (например, offers:artist:the-beatles)
  • offers:format:<slug> — офферы конкретного формата (например, offers:format:vinyl)
  • offers:style:<slug> — офферы музыкального стиля (например, offers:style:jazz)
  • articles:type:<type> — статьи определенного типа ArticleType (например, articles:type:info, articles:type:blog)
  • articles:artist:<slug> — статьи, связанные с исполнителем (например, articles:artist:the-beatles)

Примеры:

  • ["articles:type:info", "list", 50] — до 50 статей типа INFO в виде списка
  • ["offers:artist:the-beatles", "carousel", 10, "view+"] — 10 популярных релизов The Beatles в карусели

Составной режим отображения (view_mode + limit через +)

Позволяет вывести результаты одного SQL-запроса через комбинацию нескольких шаблонов (например, первые 2 элемента крупными тизерами, а остальные 15 элементов — компактным списком снизу) без дублирования блоков.

Синтаксис: "teaser+list", "2+15"

Пример:

  • ["articles:type:info", "teaser+list", "2+15", "new+", {"title": "Инструкции и справка"}]
    • Выполняется 1 SQL-запрос с LIMIT = 2 + 15 = 17.
    • Первые 2 статьи передаются в шаблон teaser.
    • Следующие 15 статей передаются в шаблон list.

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

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

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

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

Пример:

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

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

{
  "hub": [
    ["hero-cassettes-2026", "slider"],
    ["articles:type:info", "teaser+list", "2+15", "new+", {"title": "Инструкции и справочные материалы"}],
    ["offers:artist:the-beatles", "carousel", 10, "view+", {"title": "Релизы The Beatles в наличии"}],
    ["cassettes-studio", "carousel", 6, "view+"],
    ["!promo-mandarin"],
    ["offers:format:vinyl", "grid", 20, "price+"],
    ["!cart-bonus", {"threshold": 5000}]
  ]
}

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='new+'). Ошибка синтаксиса в 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-ов).

6. Архитектура исполнения DSL и Реестр обработчиков (DSL Query Registry)

Для предотвращения написания «макаронного» кода во вьюхах и обеспечения высокой расширяемости система исполнения DSL строится на основе паттерна «Реестр обработчиков» (Registry Pattern).

Принцип разделения ответственности:

  1. Вьюха hub_detail (HubView):

    • Выступает исключительно в роли входных ворот.
    • Ищет объект TbArticle по слагу и передаёт его в контекст шаблона content/hub.html.
    • Не содержит хардкода выборок, разбора параметров и формирования SQL-запросов.
  2. Template Tag {% render_hub_blocks article %}:

    • Читает массив j_article_metadata['hub'] из полученной статьи.
    • Итерируется по кортежам DSL и передаёт их в ядро исполнения DSL.
    • Вызывает шаблоны визуальных компонентов (carousel.html, grid.html, list.html, teaser.html) с контекстом, подготовленным обработчиками.
  3. Реестр обработчиков выборок (DSLQueryRegistry):

    • Модуль, содержащий карту функций-обработчиков (Query Handlers), зарегистрированных через декоратор @DSLQueryRegistry.register("prefix").
    • Каждое пространство имён 1-го позиционного параметра (articles:type, offers:artist, offers:format, offers:style, single_slug, !module) имеет свой изолированный обработчик на 3–5 строк кода.

Принцип обработки автоматических пресетов для товаров (ArticleType.ITEM):

  • Если для конкретного релиза/товара j_article_metadata['hub'] пуст, ядро DSL формирует динамический виртуальный массив блоков на основе Foreign Key связей объекта (k_item_to_artist, k_offer_to_label, k_style).
  • Виртуальные блоки прогоняются через тот же самый реестр DSLQueryRegistry, обеспечивая единообразие вывода контента без загромождения БД и дублирования логики.

film0069t-2001.08.xx