Evgenii Legotckoi
17 мая 2023 г. 1:52

Django - Урок 064. Как написать расширение для Python Markdown

Я активно использую на этом сайте и паре других проектов markdown разметку для написания текста, а потом генерирую из него html код. И в итоге я написал пару расширений, которые выполняют кое-какой специфичный функционал, который несколько облегчает поддержку генерирования html кода на моих проектах.

К примеру в одном из проектов в текст добавляются изображения, которые добавлены на сайт и прикреплены к посту. Соответственно, у изображений есть какой специфический url, по которому nginx отдаёт это изображение. Но я сделал так, что markdown код вставки изображения выглядить более просто для изображений, которые загружены на сайт.

Описание кода вставки изображения

То есть код вставки изображения в данном случае выглядит так

  1. ![](e272ed2bca9e46e8b017c20eddd1e5b2.webp)

В данном коде есть только имя файла изображения e272ed2bca9e46e8b017c20eddd1e5b2.webp . На том проекте, где это реализовано все изображения загружаются на одну директорию с уникальным генерированным именем с помощью uuid, а также изображение сжимается и конвертируется в формат WEBP. Подробнее вы можете ознакомиться, как это сделать в статье Как конвертировать изображение в формат WEBP при сохранении в ImageField .

Так вот, когда изображения прикреплены к посту, они на том проекте отображаются в виде галереии под текстом, но могут быть добавлены и внутрь текста поста. Но чтобы не смущать пользователей длинным url к изображению в markdown разметке, а также упростить дальшейшую потенциальную модификацию путей к изображению, был реализован функционал по генерированию html тега img из этого упрощённого кода изображения.

Добавление python markdown расширения

Генерирование html текста с помощью python markdown

Для генерирования HTML используется пакет Python-Markdown. Код генерироваия может выглядеть следующим образом:

  1. def generate_html(markdown_text):
  2. return markdown.markdown(
  3. markdown_text,
  4. extensions=['markdown.extensions.attr_list',
  5. 'markdown.extensions.tables',
  6. 'markdown.extensions.fenced_code',
  7. 'markdown.extensions.nl2br',
  8. 'markdown.extensions.toc'],
  9. output_format='html5'
  10. )

Как видите, при генерировании возможно перечислить расширения, которые могут влиять на результат генерирования кода. Имеено в эти расширения мы и будем добавлять наше расширение.

Добавляем расширение

Допустим в проекте на Django у нас есть app с названием core. Создадим внутри него каталог extensions с __init__.py файлом, который инициализует каталог как пакет, а также файл с названием расширения. Это должно выглядеть следующим образом.

  • core/
    • extensions/
      • __init__.py
      • image.py

Файл Image.py

А теперь добавим содержимое файла image.py .

  1. # -*- coding: utf-8 -*-
  2.  
  3. import markdown
  4. from markdown.util import etree
  5.  
  6.  
  7. class ImageExtension(markdown.Extension):
  8.  
  9. def add_inline(self, md, name, klass, re):
  10. pattern = klass(re)
  11. pattern.md = md
  12. pattern.ext = self
  13. md.inlinePatterns.add(name, pattern, "<reference")
  14.  
  15. def extendMarkdown(self, md, md_globals):
  16. self.add_inline(md, 'internal_image', InternalImage, r'!\[\]\((?P<internal_image>[a-zA-Z0-9]+).webp\)')
  17.  
  18.  
  19. class InternalImage(markdown.inlinepatterns.Pattern):
  20.  
  21. def handleMatch(self, m):
  22. return render_image(m.group('internal_image'))
  23.  
  24.  
  25. def render_image(internal_image_name):
  26. img = etree.Element('img')
  27. img.set('src', '/media/photos/{}.webp'.format(internal_image_name))
  28. img.set('class', 'd-block mx-auto mw-100 mvh-75')
  29. return img
  30.  
  31.  
  32. def makeExtension(**kwargs):
  33. return ImageExtension(**kwargs)
  34.  

В данном случае мы создаём расширение ImageExtension , которое расширяет Markdown разметку с помощью шаблона InternalImage .
Данный шаблон применяется ко всем тегам изображения, которые соответствуют регулярному выражению, которое передаётся в метод add_inline, который в итоге добавляет шаблон в список шаблонов с обработчиком кода markdown.

Функция makeExtension вызывается под капотом Python-Markdown и регистрирует расширение.

Заключение

В качестве заключительного слова добавим ваше новое расширение в генерирование фашего html кода.

  1. def generate_html(markdown_text):
  2. return markdown.markdown(
  3. markdown_text,
  4. extensions=['core.extensions.image',
  5. 'markdown.extensions.attr_list',
  6. 'markdown.extensions.tables',
  7. 'markdown.extensions.fenced_code',
  8. 'markdown.extensions.nl2br',
  9. 'markdown.extensions.toc'],
  10. output_format='html5'
  11. )

Вам это нравится? Поделитесь в социальных сетях!

ИМ
  • 5 октября 2024 г. 16:51
  • (ред.)

Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку:

  1. <ul>
  2. <li></li>
  3. <li></li>
  4. </ul>

подменять ее на свою типа:

  1. <ul class='my_ul_class'>
  2. <li class='my_li_class'></li>
  3. <li class='my_li_class'></li>
  4. </ul>
Evgenii Legotckoi
  • 31 октября 2024 г. 23:37

Добрый день.
Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь