15 KiB
Спецификация: 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/, каталоги форматов) рассматриваются как частный случай хаба:
- Вместо написания отдельной вьюхи (например,
txt_articles_list) в БД создаётся статья со слагомinfoи типомHUB. - В её DSL-конфигурации прописываются требуемые блоки (например,
[["info-articles", "list", 50]]). - Рендеринг выполняют не отдельные функции-представления, а единый универсальный диспетчер хабов (
HubView). - Каноничность URL элементов: Внутри шаблонов визуальных блоков ссылки на карточки и статьи выводится строго через
href="{{ item.get_absolute_url }}". Модель статьи/товара сама определяет свой канонический адрес по типу (например,/info/privacy-policyили/item/album-slug), исключая путаницу префиксов в ссылках независимо от того, в каком хабе опубликован блок.
2. Синтаксис и структура JSON DSL
Массив hub представляет собой список позиционных кортежей (массивов). Порядок элементов в кортеже строго фиксирован. Все элементы, кроме первого (slug / source), являются опциональными и имеют безопасные значения по умолчанию.
Позиционный порядок параметров: [ "slug", "view_mode", limit, "sort", extra ]
Описание позиций кортежа:
-
slug/source(str, 1-я позиция, обязательный):- Слаг точечной статьи/категории (например,
"cassettes-studio"). - Динамическая выборка с двоеточием (например,
"offers:artist:the-beatles","articles:type:info"). - Модуль/плагин с префиксом
!(например,"!promo-mandarin").
- Слаг точечной статьи/категории (например,
-
view_mode(str, 2-я позиция, опциональный, default зависит отArticleTypeили"grid"):- Название компонента рендеринга (шаблона):
carousel,grid,list,teaser,full,slider. - Поддерживает составной синтаксис через
+(например,"teaser+list").
- Название компонента рендеринга (шаблона):
-
limit(int | str, 3-я позиция, опциональный, default=10):- Количество выбираемых элементов (например,
10,50). - Для составных шаблонов задаётся через
+(например,"2+15").
- Количество выбираемых элементов (например,
-
sort(str, 4-я позиция, опциональный, default="new+"):- Правило сортировки элементов выборки (см. правила ниже:
new+,price+,price-,randomи т.д.).
- Правило сортировки элементов выборки (см. правила ниже:
-
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.
- Выполняется 1 SQL-запрос с
Тип 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:
- Защита от зацикливания и бесконечной рекурсии:
- Передавать множество
visited_slugsпри рендере страницы. - Если вложенный блок пытается загрузить
slug, который уже есть вvisited_slugs, пропускать блок или выводить отладочный HTML-комментарий (<!-- DSL Error: Circular reference detected for slug -->).
- Ограничение глубины вложенности:
- Жестко ограничить максимальную глубину рекурсии:
MAX_RECURSION_DEPTH = 2(устанавливается вsettings.py).
- Отказоустойчивость (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 -->.
- Изоляция данных:
- Внутри DSL запрещено использовать сырые имена полей БД (например, k_offer_to_item__s_item). Все выборки данных маппятся строго через
slugи внутренние обработчики запросов (Query Handlers).
- Валидация slug
- Добавить в валидатор админки статей (TbArticle) проверку на несовпадение slug с существующими роутами сайта из
urls.py(чтобы не было конфликта URL-ов).
film0069t-2001.08.xx