Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.0.0/),
и этот проект придерживается [Semantic Versioning](https://semver.org/lang/ru/).
## [0.2.8] - 2026-10-04
### Добавлено
- Локальная статика для внешних фронтенд-библиотек (Bootstrap 5, Bootstrap Icons, HTMX, Alpine.js, CodeMirror 6) для работы интерфейса при недоступности CDN.
- Стратегия fallback-подключения в шаблонах: автоматическое переключение на локальную статику при сбое или блокировке CDN.
- Скрипт копирования production-ассетов `frontend-assembly/copy-vendor.js` и обновление сборки через `frontend-assembly/assembly_via_npm.sh`.
- Автоматические тесты проверки наличия статических вендорных ассетов и корректности рендеринга fallback-подключений в `typograph/tests.py`.
### Изменено
- Обновлен и структурирован `.gitignore` с подробными комментариями.
## [0.2.7] - 2025-03-22
### Изменено
- Обновление всех пактов, включая `etpgrf` (v0.1.5 → v0.1.6.post1).
<p><strong>Management Command</strong> — это обычный Python-скрипт, который «встраивается» в экосистему Django. Он имеет доступ к моделям, настройкам (settings.py) и базе данных, но запускается из консоли через <code>python manage.py ...</code>.</p>
<h2>Зачем это нужно?</h2>
<p>Представьте ситуации:</p>
<h4>[0.1.6.post1] - 2026–03–19</h4>
<h5>Изменено</h5>
<ul>
<li>Нужно пересчитать рейтинг для десятки тысяч товаров на сайте или сделать типографику всех публикаций после подключения etpgrf-типографа (или любого другого).</li>
<li>Необходимо обновить цены для всех товарных карточек в соответствии с таблицей excel или по API (или спарсить данные с внешнего сайта).</li>
<li>Нужно пометить комментарии или публикации как архивные в соответствии с правилом или почистить базу от старых логов.</li>
<li>Новый алгоритм «висячей пунктуации» (<em>HangingPunctuationProcessor</em>). Добавлены компенсирующие пробелы для висячих символов, чтобы избежать наложения на соседние слова. Теперь «висячие символы» (кавычки, скобки и т. п.) оборачиваются в <code><span></code> вместе с ближайшим словом и пробелом, что обеспечивает отсутствие визуальных смещений внутри строки. Режим <code>both</code> (для одновременного вывешивания в обе стороны) отключен из-за потенциальных конфликтов компенсирующих пробелов и проблем с выравниванием при использовании CSS <code>text-justify</code>.</span></li>
</ul>
<h4>Исправлено</h4>
<ul>
<li>Автоматическая замена символов <code>x</code>, <code>X</code>, <code>х</code>, <code>Х</code>, стоящих между числами, на знак умножения <code>×</code>, чтобы выражения вида <code>100x100</code> или <code>100 х 100` корректно обрабатывались и выглядели типографски правильными (<code>100×100</code> или <code>100&nbsp;×&nbsp;100</code>).</li>
</ul>
<p>Делать это через <tt>views.py</tt> (views) — плохая идея (страница может отвалиться по тайм-ауту). Писать отделный скрипт рядом <tt>manage.py</tt> — неудобно (нужно вручную настраивать `DJANGO_SETTINGS_MODULE`). Встроенные команды решают эти проблемы элегантно.</p>
<h2>Как это работает?</h2>
<p>Django использует систему «автообна­ружения» (auto-discovery). Чтобы ваша команда появилась в списке `manage.py`, нужно соблюсти строгую иерархию папок внутри вашего приложения (например, <tt>web</tt>):</p>
<p>Файл <tt>my_cool_script.py</tt> автома­тически превратится в команду: <code>python manage.py my_cool_script</code>. Django будет искать его в папках <tt>management/commands/</tt> внутри каждого устано­вленного приложения. Это позволяет легко организовать код и держать все «команды обслуживания» в одном месте.</p>
<p><strong>Для справки:</strong> Механизм Custom Management Commands появился еще в Django 0.96 (в глубокой древности) и с тех пор является стандартом де-факто для написания скриптов обслуживания.</p>
<h2>Анатомия команды</h2>
<p>Вот пример простейшей команды. Мы наследуемся от класса <code>BaseCommand</code> и переопре­деляем метод <code>handle</code>:</p>
<p>В моём проекте, <ahref="https://dq.cube2.ru/"target="_blank">DQ – коллекция цитат: место для вдумчивого чтения</a>, контент был частично обработан с помощью Типографа Муравьёа, частично вручную. И вот я установил и настроил etpgrf (тем более что Типограф Муравьёва «почил в бозе», да и до того не обновлялся с 2018 года). Все новые цитаты типогра­фируются через etpgrf, но что делать со старыми? Их сотни, и открывать и «пересо­хранаять» каждую вручную — это адский труд. Вот тут на помощь и приходит Custom Management Command.</p>
<p>Возможно вы заметили, что в моем проекте для контента есть два поля: <code>szContent</code> и <code>szContentHTML</code>. В первом хранится «сырой» текст, во втором — результат типогра­фирования. В вашем проекте, скорее всего, только одно поле для контента, но по сути это ничего не меняет.</p>
<p>Еще интересные фишки, которые исполь­зовались при типогра­фировании:</p>
<ol>
<li><code>self.stdout.write</code> вместо <code>print</code> – позволяет Django перехва­тывать вывод (например, для тестов) и корректно работать с кодировками.</li>
<li><code>self.style.SUCCESS</code> и <code>self.style.ERROR</code> – раскрашивает текст в консоли (зеленый/красный) и это очень удобно для визуального восприятия логов.</li>
<li>Аргументы <tt>--limit</tt> и <tt>--offset</tt> — позволяют обрабатывать базу «порциями», что полезно для больших объемов данных (можно запустить несколько процессов параллельно с разными <tt>offset</tt>).</li>
<li>Используется <code>update_fields</code> — позволяет переза­писывать не всю модель целиком (что могло бы затереть изменения, сделанные кем-то другим в ту же секунду), а обновляем только конкретные поля.</li>
</ol>
<p>Теперь, чтобы типогра­фировать весь контент, нам достаточно одной строку в терминале, и Django сделает всю грязную работу за нас (не забудьте инициировать виртуальное окружение вашего проекта).</p>
<p>Тестовый прогон (безопасно, ничего не сохраняет):</p>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.