Evgenii Legotckoi
Evgenii Legotckoi16 мая 2023 г. 15:52

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

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

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

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

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

![](e272ed2bca9e46e8b017c20eddd1e5b2.webp)

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

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

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

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

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

def generate_html(markdown_text):
    return markdown.markdown(
                markdown_text,
                extensions=['markdown.extensions.attr_list',
                            'markdown.extensions.tables',
                            'markdown.extensions.fenced_code',
                            'markdown.extensions.nl2br',
                            'markdown.extensions.toc'],
                output_format='html5'
            )

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

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

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

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

Файл Image.py

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

# -*- coding: utf-8 -*-

import markdown
from markdown.util import etree


class ImageExtension(markdown.Extension):

    def add_inline(self, md, name, klass, re):
        pattern = klass(re)
        pattern.md = md
        pattern.ext = self
        md.inlinePatterns.add(name, pattern, "<reference")

    def extendMarkdown(self, md, md_globals):
        self.add_inline(md, 'internal_image', InternalImage, r'!\[\]\((?P<internal_image>[a-zA-Z0-9]+).webp\)')


class InternalImage(markdown.inlinepatterns.Pattern):

    def handleMatch(self, m):
        return render_image(m.group('internal_image'))


def render_image(internal_image_name):
    img = etree.Element('img')
    img.set('src', '/media/photos/{}.webp'.format(internal_image_name))
    img.set('class', 'd-block mx-auto mw-100 mvh-75')
    return img


def makeExtension(**kwargs):
    return ImageExtension(**kwargs)

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

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

Заключение

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

def generate_html(markdown_text):
    return markdown.markdown(
                markdown_text,
                extensions=['core.extensions.image',
                            'markdown.extensions.attr_list',
                            'markdown.extensions.tables',
                            'markdown.extensions.fenced_code',
                            'markdown.extensions.nl2br',
                            'markdown.extensions.toc'],
                output_format='html5'
            )
Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

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

Комментарии

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

C++ - Тест 006. Перечисления

  • Результат:10баллов,
  • Очки рейтинга-10
K
  • KiRi4
  • 7 сентября 2023 г. 7:57

C++ - Тест 002. Константы

  • Результат:41баллов,
  • Очки рейтинга-8
K
  • KiRi4
  • 7 сентября 2023 г. 7:49

C++ - Тест 001. Первая программа и типы данных

  • Результат:66баллов,
  • Очки рейтинга-1
Последние комментарии
IscanderChe
IscanderChe13 сентября 2023 г. 9:11
Пример использования QScintilla C++ По горячим следам (с другого форума вопрос задали, пришлось в памяти освежить всё) решил дополнить. Качаем исходники с https://riverbankcomputing.com/software/qscintilla/downlo…
Evgenii Legotckoi
Evgenii Legotckoi6 сентября 2023 г. 7:18
Qt/C++ - Урок 048. QThread - работа с потоками с помощью moveToThread Разве могут взаимодействовать объекты из разных нитей как-то, кроме как через сигнал-слоты?" Могут. Выполняя оператор new , Вы выделяете под объект память в куче (heap), …
AC
Andrei Cherniaev5 сентября 2023 г. 3:37
Qt/C++ - Урок 048. QThread - работа с потоками с помощью moveToThread Я поясню свой вопрос. Выше я писал "Почему же в методе MainWindow::on_write_1_clicked() Можно обращаться к методам exampleObject_1? Разве могут взаимодействовать объекты из разных…
n
nvn31 августа 2023 г. 9:47
QML - Урок 004. Сигналы и слоты в Qt QML Здравствуйте! Прекрасный сайт, отличные статьи. Не хватает только готовых проектов для скачивания. Многих комментариев типа appCore != AppCore просто бы не было )))
NSProject
NSProject24 августа 2023 г. 13:40
Django - Урок 023. Like Dislike система с помощью GenericForeignKey Ваша ошибка связана с gettext from django.utils.translation import gettext_lazy as _ Поле должно выглядеть так vote = models.SmallIntegerField(verbose_name=_("Голос"), choices=VOTES) …
Сейчас обсуждают на форуме
IscanderChe
IscanderChe17 сентября 2023 г. 9:24
Интернационализация строк в QMessageBox Странная картина... Сделал минимально работающий пример - всё работает. Попробую на другой операционке. Может, дело в этом.
NSProject
NSProject17 сентября 2023 г. 8:49
Помогите добавить Ajax в проект В принципе ничего сложного с отправкой на сервер нет. Всё что ты хочешь отобразить на странице передаётся в шаблон и рендерится. Ты просто создаёшь файл forms.py в нём описываешь свою форму и в …
BlinCT
BlinCT15 сентября 2023 г. 12:35
Размеры полей в TreeView Всем привет. Пытаюсь сделать дерево вот такого вида Пытаюсь организовать делегат для каждой строки в дереве. ТО есть отступ какого то размера и если при открытии есть под…
IscanderChe
IscanderChe8 сентября 2023 г. 12:07
Кастомная QAbstractListModel и цвет фона, цвет текста и шрифт Похоже надо не абстрактный , а "реальный" типа QSqlTableModel Да, но не совсем. Решилось с помощью стайлшитов и setFont. Спасибо за отлик!
Evgenii Legotckoi
Evgenii Legotckoi6 сентября 2023 г. 6:35
Вопрос: Нужно ли в деструкторе удалять динамически созданные QT-объекты. Напр: Зависит от того, как эти объекты были созданы. Если вы передаёте указатель на parent объект, то не нужно, Ядро Qt само разрулит удаление, если нет, то нужно удалять вручную, иначе будет ут…

Следите за нами в социальных сетях