mp: Чистые, типографированные заголовки в админке Django: убираем HTML-мнемоники (вёрстка в песочнице).
This commit is contained in:
@@ -21,208 +21,53 @@
|
|||||||
{# Правая колонка: Контент #}
|
{# Правая колонка: Контент #}
|
||||||
<div class="col-lg-10 border-start ps-lg-4 post-page-content">
|
<div class="col-lg-10 border-start ps-lg-4 post-page-content">
|
||||||
|
|
||||||
<h1>Журнал изменений</h1>
|
<h1>Чистые, типогра­фированные заголовки в админке Django: убираем HTML-мнемоники</h1>
|
||||||
|
|
||||||
<div class="lead bg-secondary bg-opacity-10 p-3 rounded">
|
<div class="lead bg-secondary bg-opacity-10 p-3 rounded">
|
||||||
<p>На этой странице задокумен­тированы заметные изменения в проекте: настоящего сайта для онлайн-типографа и библиотеки etpgrf.</p>
|
<p>Библиотека типографа <tt>etpgrf</tt> написана на Python, и потому его исполь­зование в Django — наиболее распрос­траненный случай. Когда в Django вы храните в базе данных типогра­фированный текст (с HTML-мнемониками: неразрывными пробелами <code>&nbsp;</code>, мягкими переносами <code>&shy;</code> и тому подобное), то стандартный список в админке Django превращается в не очень удобо­читаемую кашу из спецсимволов. Рассказываем, как это исправить за пару строк кода.</p>
|
||||||
<p dir="auto">Формат основан на <a href="https://keepachangelog.com/en/1.0.0/" rel="nofollow" target="_blank">Keep a Changelog</a>, и придер­живается <a href="https://semver.org/spec/v2.0.0.html" rel="nofollow" target="_blank">Semantic Versioning</a>.</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<div class="row align-items-start">
|
<div class="row align-items-start">
|
||||||
<div class="col border-end">
|
<div class="col border-end">
|
||||||
<h3>Сайт typograph.cube2.ru</h3>
|
|
||||||
|
|
||||||
<p>Исходный код сайта онланй-типографа <tt>etpgrf</tt> доступен в нескольких репози­ториях (<a href="https://github.com/erjemin/etpgrf-site" target="_blank">GitHub</a>, <a href="https://gitverse.ru/erjemin/etpgrf-site" target="_blank">GitVerse</a> и <a href="https://git.cube2.ru/erjemin/2026-etpgrf-site" target="_blank">Сube2</a>. Наиболее заметные изменения сайта онлайн-типографа приводятся ниже в хронологическом порядке. Для полного списка изменений, включая мелкие фиксы и коммиты, пожалуйста, обратитесь к истории репозитория.</p>
|
<h2>Проблема</h2>
|
||||||
|
<p>Представьте, что у вас есть модель <code>Post</code> в которой хранятся записи блога, и вы прогнали заголовки через типограф (например, через наш <a href="/">онлайн-типограф</a> или с помощью библиотеки <tt>etpgrf</tt>). В базе данных заголовок выглядит так:</p>
|
||||||
|
<pre class="border p-3 my-3 bg-secondary bg-opacity-25">Политика безопасности и&nbsp;конфи&shy;денциаль&shy;ности</pre>
|
||||||
|
<p>На сайте это будет отлично выглядеть для браузера, но в панели админис­тратора или контент-менеджера это ужасно, так как Django «маскирует» <code>&</code> и все HTML-мнемоники будут видны во всей красе. В списке объектов (Change List) будет виден «сырой» HTML-код, и читать такое сложно, особенно если мнемоник много.</p>
|
||||||
|
<h2>Решение</h2>
|
||||||
|
<p>Нам нужно, чтобы в списке (<code>list_display</code>) отображался «чистый» текст, а в форме редакти­рования оставался исходный HTML (чтобы его было легче править).</p>
|
||||||
|
<p>Для этого мы добавим в класс <code>ModelAdmin</code> специальный метод, который будет декодировать HTML-мнемоники перед выводом в админку.</p>
|
||||||
|
<h3>Шаг 1. Импортируем модуль html</h3>
|
||||||
|
<p>В Python есть встроенная библиотека <code>html</code>, которая умеет делать <code>unescape</code> — превращать мнемоники, например <code>&nbsp;</code> или <code>&shy;</code>, unicode. <code>&nbsp;</code> превратится в неразрывный пробел, а <code>&shy;</code> в мягкий перенос.</p>
|
||||||
|
<h3>Шаг 2. Создаем метод в админке</h3>
|
||||||
|
<p>В файле <code>admin.py</code>:</p>
|
||||||
|
<pre class="border p-3 my-3 rounded bg-secondary bg-opacity-25">from django.contrib import admin
|
||||||
|
import html
|
||||||
|
from .models import Post
|
||||||
|
|
||||||
<h4>[0.2.5] — 2025–02–13</h4>
|
@admin.register(Post)
|
||||||
<h5>Добавлено</h5>
|
class PostAdmin(admin.ModelAdmin):
|
||||||
<ul>
|
# В list_display указываем не поле 'title', а наш метод 'clean_title'
|
||||||
<li>Редизайн списка постов в блоге: шахматный порядок, вертикальные разделители, улучшенная адаптивность для мобильных
|
list_display = ('clean_title', 'is_published', 'published_at')
|
||||||
устройств.
|
|
||||||
</li>
|
|
||||||
<li>Поле <code>updated_at</code> (<em>Дата обновления</em>) в модели, админке, блогах, страницах, <tt>sitemaps.xml</tt> и микро­разметке <tt>Schema.org</tt> для улучшения <abbr title="Search Engine Optimization / Поисковая оптимизация">SEO</abbr>, <abbr title="Generative Engine Optimization / Оптимизацию для Генеративных нейросетей и ИИ-поисковиков">GEO</abbr> и <abbr title="Large Language Model Optimization / Оптимизация под Большие Языковые Модели">LLMO</abbr>.
|
|
||||||
</li>
|
|
||||||
<li><tt>README.md</tt> с описанием проекта онлайн-типографа, его особенностей, технического стека и инструкциями по установке и запуску.
|
|
||||||
</li>
|
|
||||||
<li>Отображение заголовков постов в списке админки без HTML-мнемоник (декоди­рование <code>&nbsp;</code> и др.).
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<h5>Изменено</h5>
|
|
||||||
<ul>
|
|
||||||
<li>Исправлены ошибки в шаблоне <tt>post_list.html</tt> (и полностью переработан дизайн в целом).
|
|
||||||
<ul>
|
|
||||||
<li>Улучшено отображение даты и скрытие декоративных изображений в списке постов на мобильных устройствах.
|
|
||||||
</li>
|
|
||||||
<li>Оптими­зированы отступы и типографика в списке постов.</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>Формирование <code>slug</code> из <code>title</code> при сохранении поста или страницы с исполь­зованием библиотеки <code>pytils</code> для трансли­терации с очистикой от HTML-мнемоник и создания <abbr title="Uniform Resource Locator / Унифицированный Указатель Ресурса">URL</abbr>-дружес­твенных строк.
|
|
||||||
</li>
|
|
||||||
<li>Дизайн и вёрстка страниц для постов блога и вспомо­гательных страниц для мобильных устройств (адаптивность, скрытие картинки-обложки).
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h4>[0.2.4] — 2025–02–12</h4>
|
# ... остальные настройки ...
|
||||||
<h5>Добавлено</h5>
|
|
||||||
<ul>
|
|
||||||
<li>Микро­разметка <code>Schema.org</code> (JSON-LD) для постов и страниц для улучшения
|
|
||||||
SEO и понимания контента поисковиками и ИИ.
|
|
||||||
</li>
|
|
||||||
<li>Файл <code>llms.txt</code> для предоста­вления информации о сайте и API для больших
|
|
||||||
языковых моделей (<abbr title="Large Language Model">LLM</abbr>) в соответвиии со стандартом GEO
|
|
||||||
и LLMO.
|
|
||||||
</li>
|
|
||||||
<li>Кастомный фильтр <code>unescape</code> для очистки мета-тегов от HTML-сущностей и переводов
|
|
||||||
строк.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<h5>Исправлено</h5>
|
|
||||||
<ul>
|
|
||||||
<li>Исправлена ошибка, при которой счетчик символов не обновлялся при восста­новлении вкладки
|
|
||||||
из истории браузера.
|
|
||||||
</li>
|
|
||||||
<li>Исправлена ошибка экрани­рования кавычек в <tt>JSON-LD</tt>, <tt>Title</tt> и <tt>Description</tt>.
|
|
||||||
</li>
|
|
||||||
<li>В <code>docker-compose.yaml</code> додавлен перезапуск <code>watchtower</code> при его
|
|
||||||
остановке.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<h4>[0.2.3] — 2025–02–11</h4>
|
|
||||||
<h5>Изменено</h5>
|
|
||||||
<ul>
|
|
||||||
<li>Добавлена кнопка очистки текста во вводном поле и счетчик вводимых символов.</li>
|
|
||||||
</ul>
|
|
||||||
<h4>[0.2.2] — 2025–02–03</h4>
|
|
||||||
<h5>Изменено</h5>
|
|
||||||
<ul>
|
|
||||||
<li>В онлайн-типографе подключена новая версия библиотеки etpgrf (v0.1.3 → v0.1.4).</li>
|
|
||||||
<li>Незначи­тельные улучшения в оформлении.</li>
|
|
||||||
</ul>
|
|
||||||
<h4>v0.2.1 — 2026–01–30</h4>
|
|
||||||
<h5>Исправлено</h5>
|
|
||||||
<ul>
|
|
||||||
<li>Исправление ошибок при формировании мета-тегов, картинок, <code>alt</code> под картинками
|
|
||||||
и т. п.
|
|
||||||
</li>
|
|
||||||
<li>Исправлена ошибка в настройках nginx внутри docker-контейнера возникавшая при отдаче
|
|
||||||
media-файлов.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<h5>Изменено</h5>
|
|
||||||
<ul>
|
|
||||||
<li>При создании записи в блог или страницы «тизер» обязателен!</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h4>v0.2.0 — 2026–01–28</h4>
|
# Декоратор @admin.display позволяет настроить название колонки и сортировку
|
||||||
<h5>Добавлено</h5>
|
@admin.display(description='Заголовок', ordering='title')
|
||||||
<ul>
|
def clean_title(self, obj):
|
||||||
<li>Приложение blog (для страниц и постов) и соответ­ствующие изменения в моделях базы, добавление новых view и шаблонов.</li>
|
# Декодируем HTML-сущности ( -> U+00A0)
|
||||||
<li>Песочница (шаблон <code>blog/templates/blog/tmp.html</code> для тестирования верстки (доступен только в режиме debug).</li>
|
return html.unescape(obj.title)
|
||||||
<li>Динамическое создание sitemap.xml</li>
|
</pre>
|
||||||
<li>robots.txt</li>
|
<h2>Как это работает?</h2>
|
||||||
<li>Изменения в шапке сайта (меню и бургер)</li>
|
|
||||||
</ul>
|
|
||||||
<h5>Изменено</h5>
|
|
||||||
<ul>
|
|
||||||
<li>Спрятан <code>url</code> админки типографа. Его расположение теперь задается через переменные окружения в <code>.env</code>.</li>
|
|
||||||
<li><code>favicon.ico</code> оптими­зирована для Яндекс (120х120px).</li>
|
|
||||||
<li>Исправлено поведение шапки и логотива для мобильных устройств.</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h4>v0.1.8 — 2026–01–23</h4>
|
|
||||||
Коммит:
|
|
||||||
846c066
|
|
||||||
|
|
||||||
<h4>v0.1.7 — 2026–01–23</h4>
|
|
||||||
Коммит:
|
|
||||||
d74bee2
|
|
||||||
|
|
||||||
<h4>v0.1.6 — 2026–01–23</h4>
|
|
||||||
Коммит:
|
|
||||||
6b4dbaf
|
|
||||||
|
|
||||||
<h4>v0.1.5 — 2026–01–21</h4>
|
|
||||||
Коммит:
|
|
||||||
78174a8
|
|
||||||
|
|
||||||
<h4>v0.1.4 — 2026–01–20</h4>
|
|
||||||
Коммит:
|
|
||||||
2d09aef
|
|
||||||
|
|
||||||
<h4>v0.1.3 — 2026–01–19</h4>
|
|
||||||
Коммит:
|
|
||||||
66f2228
|
|
||||||
|
|
||||||
<h4>v0.1.2 — 2026–01–18</h4>
|
|
||||||
Коммит:
|
|
||||||
92711f5
|
|
||||||
|
|
||||||
<h4>v0.1.1 — 2026–01–16</h4>v0.1.1
|
|
||||||
Коммит:
|
|
||||||
5d5d48d
|
|
||||||
|
|
||||||
<h4>v0.1.0 — 2026–01–16</h4>
|
|
||||||
Коммит:
|
|
||||||
3a7bb29
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="col border-start">
|
|
||||||
<h3>Библиотека etpgrf</h3>
|
|
||||||
<p>
|
|
||||||
Исходный код etpgrf-типографа доступен в нескольких репози­ториях (<a href="https://github.com/erjemin/etpgrf" target="_blank">GitHub</a>, <a href="https://gitverse.ru/erjemin/etpgrf" target="_blank">GitVerse</a>, <a href="https://git.cube2.ru/erjemin/2025-etpgrf" target="_blank">Сube2</a>, и <a href="https://pypi.org/project/etpgrf/" target="_blank">PyPI</a>), распрос­траняется под лицензией <a href="https://opensource.org/licenses/MIT" target="_blank">MIT</a>, может быть установлен локально, на ваш сайт или интегри­рован в ваши проекты как Python-библиотека.
|
|
||||||
</p>
|
|
||||||
<h4>[0.1.4] - 2025–02–03</h4>
|
|
||||||
<h5>Изменено</h5>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><b>Архите­ктурное улучшение:</b> Полностью переработан механизм обработки HTML.</li>
|
<li>Метод <code>clean_title</code> получает объект модели.</li>
|
||||||
<li>Внедрены <b>маркеры границ узлов</b> (<code>\uFFFF</code>) при сборке текста. Это позволяет корректно восста­навливать структуру HTML даже если длина текста изменилась в процессе обработки (например, при удалении лишних пробелов).</li>
|
<li><code>html.unescape(obj.title)</code> превращает HTML-мнемоники в Unicode: <code>&nbsp;</code> в символ неразрывного пробела (<tt>U+00A0</tt>); <code>&shy;</code> — в мягкий перенос (<tt>U+00AD</tt>) и так далее.</li>
|
||||||
<li>Внедрены <b>плейсхолдеры</b> (<code>\uFFFC</code>) для защищенных тегов (<code><code></code>, <code><script></code> и др.). Теперь содержимое этих тегов физически изолируется перед обработкой, что предот­вращает «протекание» контекста (например, склеивание слов, разделенных кодом).</li>
|
<li>Unicode не маскируется адимнкой Django, символы выглядят как обычный текст (или вообще не видны), поэтому список становится чистым и читаемым (и даже, более того, в списке уже будут типогра­фированные поля <code>title</code>).</li>
|
||||||
|
<li>При этом сортировка по колонке (<code>ordering='title'</code>) продолжает работать по ориги­нальному полю в базе данных.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h5>Исправлено</h5>
|
<p>Теперь админка выглядит опрятно, а типографика на сайте остается безупречной!</p>
|
||||||
<ul>
|
|
||||||
<li>Исправлена ошибка смещения текста при наличии спецсимволов (мнемоник) или при изменении длины строки.</li>
|
|
||||||
<li>Исправлена обработка кавычек, стоящих вплотную к границам тегов (например, <code>"<b>Текст</b>"</code>).</li>
|
|
||||||
</ul>
|
|
||||||
<h4>v0.1.3 — 2026–01–11</h4>
|
|
||||||
<h5>Исправлено</h5>
|
|
||||||
<ul>
|
|
||||||
<li>Исправлена проблема с появлением лишних тегов <code><html></code> и <code><body></code> при обработке фрагментов HTML (когда исполь­зуется парсер <code>lxml</code>). Теперь типограф автома­тически определяет, был ли на входе полноценный документ или фрагмент, и возвращает соответ­ствующий результат.</li>
|
|
||||||
</ul>
|
|
||||||
<h4>v0.1.2 — 2025–12–27</h4>
|
|
||||||
<h5>Исправлено</h5>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Критическое исправление:</strong> Добавлена отсут­ствующая зависимость <code>regex</code> в <code>pyproject.toml</code>. Без неё библиотека падала при импорте.</li>
|
|
||||||
</ul>
|
|
||||||
<h4>v0.1.1 — 2025–12–23</h4>
|
|
||||||
<h5>Добавлено</h5>
|
|
||||||
<ul>
|
|
||||||
<li>Ссылки на зеркала репозитория (GitVerse, Gitea) в <code>pyproject.toml</code> и <code>README.md</code>.</li>
|
|
||||||
<li>Раздел Credits в документации.</li>
|
|
||||||
</ul>
|
|
||||||
<h4>v0.1.0 — 2025–12–23</h4>
|
|
||||||
<h5>Добавлено</h5>
|
|
||||||
<ul>
|
|
||||||
<li>Первый публичный релиз библиотеки <code>etpgrf</code>.</li>
|
|
||||||
<li>
|
|
||||||
Основные модули:
|
|
||||||
<ul>
|
|
||||||
<li><code>Typographer</code>: основной класс-оркестратор.</li>
|
|
||||||
<li><code>Hyphenator</code>: расстановка мягких переносов (алгоритм Ляна-Кнута).</li>
|
|
||||||
<li><code>QuotesProcessor</code>: замена кавычек («ёлочки», „лапки“).</li>
|
|
||||||
<li><code>Unbreakables</code>: неразрывные пробелы для предлогов, союзов и частиц.</li>
|
|
||||||
<li><code>LayoutProcessor</code>: типографика тире, инициалов, акронимов, единиц измерения, устойчивых сокращений (постпо­зиционных и препози­ционных).</li>
|
|
||||||
<li><code>SymbolsProcessor</code>: псевдо­графика (тире, стрелочки, копирайт и т. п.)</li>
|
|
||||||
<li><code>HangingPunctuationProcessor</code>: висячая пунктуация.</li>
|
|
||||||
<li><code>SanitizerProcessor</code>: очистка HTML перед обработкой.</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>Поддержка русского, русского дорефо­рменного и английского языков.</li>
|
|
||||||
<li>Поддержка обработки HTML (через BeautifulSoup).</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user