Evgenii Legotckoi
May 17, 2023, 1:52 a.m.

Django - Lesson 064. How to write a Python Markdown extension

On this site and a couple of other projects, I actively use markdown to write text, and then generate html code from it. And in the end, I wrote a couple of extensions that perform some specific functionality, which somewhat facilitates the support for generating html code on my projects.

For example, in one of the projects, images are added to the text that are added to the site and attached to the post. Accordingly, images have a specific url that nginx sends this image to. But I made the markdown image embed code look simpler for images that are uploaded to the site.

Image embed code description

That is, the image insertion code in this case looks like this

![](e272ed2bca9e46e8b017c20eddd1e5b2.webp)

In this code, there is only the name of the image file e272ed2bca9e46e8b017c20eddd1e5b2.webp . On the project where this is implemented, all images are loaded into one directory with a unique generated name using uuid, and the image is compressed and converted to WEBP format. You can learn more about how to do this in the article How to convert an image to WEBP format when saving to an ImageField .

So, when images are attached to a post, they are displayed on that project as a gallery under the text, but can also be added inside the post text. But in order not to confuse users with a long url to the image in markdown markup, and also to simplify further potential modification of image paths, a function was implemented to generate an html img tag from this simplified image code.

Adding python markdown extension

Generate html text with python markdown

The Python-Markdown package is used to generate the HTML. The generation code might look like this:

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'
            )

As you can see, when generating it is possible to enumerate extensions that can affect the result of code generation. It is in these extensions that we will add our extension.

Add extension

Let's say in a Django project we have an app called core. Let's create an extensions directory inside it with a __init__.py file that initializes the directory as a package, as well as a file with the name of the extension. It should look like this.

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

Image.py file

Now let's add the contents of the image.py file.

# -*- 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)

In this case, we are creating an ImageExtension extension that extends the Markdown markup using the InternalImage template.
This pattern is applied to all image tags that match the regular expression that is passed to the add_inline method, which ultimately adds the pattern to the list of patterns with the markdown code handler.

The makeExtension function is called under the hood of Python-Markdown and registers the extension.

Conclusion

As a final word, let's add your new extension to the generation of your html code.

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'
            )

Do you like it? Share on social networks!

ИМ
  • Oct. 5, 2024, 4:51 p.m.
  • (edited)

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

<ul>
    <li></li>
    <li></li>
</ul>

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

<ul class='my_ul_class'>
    <li class='my_li_class'></li>
    <li class='my_li_class'></li>
</ul>
Evgenii Legotckoi
  • Oct. 31, 2024, 11:37 p.m.

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

Comments

Only authorized users can post comments.
Please, Log in or Sign up
  • Last comments
  • Evgenii Legotckoi
    March 9, 2025, 9:02 p.m.
    К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
  • VP
    March 9, 2025, 4:14 p.m.
    Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…
  • ИМ
    Nov. 22, 2024, 9:51 p.m.
    Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
  • Evgenii Legotckoi
    Oct. 31, 2024, 11:37 p.m.
    Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
  • A
    Oct. 19, 2024, 5:19 p.m.
    Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html