在Django应用程序中渲染Markdown内容

在本教程中,您将学习 如何在Django中渲染Markdown内容,以便网站的内容创建者可以利用Markdown的简单和便利性来格式化基于文本的内容。本教程假设您具有一些Django经验,因此如果您对框架完全不熟悉,可以在这里 (opens new window)学习如何构建您的第一个Django应用。

在Django应用程序中,引入Markdown可以提升那些更喜欢使用纯文本格式化其写作的用户的内容管理工作流程,而不是使用所见即所得的编辑器。在本教程的这个部分,您将学习如何扩展Django应用程序以允许创建、管理和渲染Markdown内容。

您将通过设置一个Django项目和应用程序,定义一个用于存储Markdown内容的模型, 创建Django模板以将内容呈现为HTML。此外,您还将探索在Django视图中使用Python-Markdown将Markdown转换为HTML。

在本节结束时,您将在Django应用程序中拥有一个完全功能的Markdown内容管理系统。

创建一个Django应用程序来显示内容 #

要使用Django呈现以Markdown编写的HTML内容,请首先在Python虚拟环境中创建一个名为dmd_project的新Django项目。您可以使用dmd作为Django Markdown的简写:

此教程所需的唯一依赖项是Django和Python-Markdown (opens new window)包,它提供了在Python中处理Markdown的API。[安装markdown](https://pyt 使用pip (opens new window)在虚拟环境中安装hon-markdown.github.io/install/

现在使用manage.py startapp命令创建一个新的Django应用程序:

您已经使用startapp命令创建了dmd_app/文件夹,其中包含通过Django管理界面组成内容所需的文件,将其存储在项目的数据库中,并以HTML格式显示给用户。将新的应用程序添加到项目的settings.py文件中的INSTALLED_APPS设置中:

Python dmd_project/settings.py

为了创建一个新的数据库表来存储项目的内容,请在dmd_app/目录下的models.py文件中定义一个MarkdownContent模型:

您的MarkdownContent模型包含两个字段:一个限制为一百个字符的title字段和一个可以包含无限量文本的content字段。 content字段是您将存储基于Web的Markdown内容的位置。

在定义了模型之后,运行makemigrationsmigrateDjango管理命令,它将您的Python代码转换为SQL (opens new window)并在项目的数据库上执行以创建表:

现在,您已经定义了MarkdownContent模型,将其注册到admin模块中。这样,您的网站内容创作者就可以登录并通过管理界面来撰写内容:

为了登录到管理员网站,您需要为自己创建一个新的超级用户帐户。在输入以下命令后,请按照控制台上的提示进行操作:

使用runserverDjango管理命令启动您的本地开发服务器,然后在浏览器中导航到http://127.0.0.1:8000/admin/。现在,您应该能够看到管理员页面。 在登录界面中,您可以输入您刚创建的用户名和密码。登录以查看包含用于创建MarkdownContent模型的数据库记录的管理界面:

Django管理界面 (opens new window)

您的Markdown内容模型的Django管理界面部分

点击“Markdown内容”链接,然后点击屏幕右上角的“ADD MARKDOWN CONTENT”按钮:

Django Admin添加新内容 (opens new window)

将您的Markdown内容添加到Django管理界面的位置

在表单字段中输入标题和内容,创建一个新的MarkdownContent记录,并选择“保存”,这将重定向您到MarkdownContent模型的管理列表视图。 Django Admin List View (opens new window)

Django管理列表视图显示您的Markdown内容模型实例

现在您已经创建了一些初始内容,您可以添加您的Django项目所需的最终组件,以便为用户显示该内容:

首先,创建一个新的文件夹来存储您的Django模板文件,路径为 dmd_app/templates/。然后,在 templates/ 文件夹内创建一个名为 dmd_app/ 的子文件夹,并在其中创建一个名为 markdown_content.html 的新文件。

这种文件夹嵌套的约定有助于Django模板加载器找到正确的模板 (opens new window) 通过为这个Django应用程序创建一个唯一的模板命名空间来创建一个特定于该应用程序的模板。将以下代码添加到该文件中:

HTML dmd_app/templates/dmd_app/markdown_content.html

在这里,您使用了Django模板变量语法创建了一个HTML模板,当在浏览器中渲染页面时,Django会用您的MarkdownContent模型实例的值替换它。但首先,您需要在Django视图文件中从数据库中加载您的模型实例。

在您的dmd_app/目录的views.py文件中,定义一个视图函数来检索刚刚创建的MarkdownContent记录作为模型实例,并将该实例作为上下文变量传递以在您的模板中渲染:

这段代码使用.objects.first()来检索在Django admin中创建的单个MarkdownContent实例。请注意,如果您还没有创建MarkdownContent模型实例,这段视图代码将失败。 你将在本教程中改进此代码,但现在,请确保在运行之前按照上述步骤在admin中创建你的初始模型实例。

在你的项目的urls.py文件中,向urlpatterns列表中添加一个新的path对象,以定义你的内容页面的URL模式:

Python dmd_project/urls.py

你添加到urlpatternspath对象包括三个参数。你有URL字符串,Django在用户在浏览器中导航到URL时将调用的视图函数markdown_content_view(),以及在Django模板中使用的唯一URL名称。

现在,如果你在浏览器中导航到http://127.0.0.1:8000/markdown-content/,你应该可以看到你在admin中创建的内容。现在,你可以为你的Django项目添加Markdown渲染功能,所有必要的部件都已就位,可以创建、保存和显示网站内容。 重新登录管理员账户,选择“Markdown内容”,然后点击链接,从标题为“选择要更改的Markdown内容”的列表中选择你的模型实例。将“内容”字段中输入的文本替换为包含Markdown的新文本:

这段文本使用Markdown语法创建了标题、段落、无序列表和代码块。在用包含Markdown的文本替换内容后,点击“保存”:

Markdown内容 (opens new window)

在Django管理员中编辑你的内容以使用Markdown

在浏览器中返回http://127.0.0.1:8000/markdown-content/,你将看到一团糟:

带有语法符号的Markdown (opens new window)

带有Markdown语法的文本显示的是一团糟。 在浏览器中显示

但是您可以使用您在项目的初始设置中安装的Python-Markdown包来清理它。在您的dmd_app/views.py文件中,导入markdown模块并编辑您的代码如下:

在这里,您在视图函数中初始化了markdown.Markdown,将fenced_code选项传递给extensions参数,这允许您显示格式化的代码块。然后,您调用.convert()方法将markdown_content模型实例的content属性转换为HTML。但是现在,如果您在http://127.0.0.1:8000/markdown-content/加载页面,那么您将在屏幕上看到实际的HTML标记:

在浏览器中显示的HTML (opens new window)

在浏览器中显示带有HTML标记的文本

您可以通过添加Django的内置[safe](https://docs.djangoproject.com/ 使用以下Markdown代码,并删除第一级标题:en/4.2/ref/templates/builtins/#std-templatefilter-safe)过滤器到模板文件中的markdown_content.content变量:

HTML dmd_app/templates/dmd_app/markdown_content.html

这个内置模板过滤器 (opens new window)告诉Django模板引擎content字段的动态内容来自可信任的来源,并且可以安全地转换为HTML。现在,您的内容应该可以正确地呈现给您网站的用户:

Properly Rendered Markdown (opens new window)

在浏览器中显示的正确格式化的HTML文本

现在您已经在Django管理界面中使用了Markdown格式。如果您想在网站的其他页面上格式化内容,可以在下一部分中创建一个自定义模板过滤器,这样可以更具可重用性。 这种Markdown格式化方法在Django应用程序中是一种快速而临时的解决方案。但它与此特定用例的视图和模型代码紧密耦合。如果你想在网站的其他地方使用Markdown,那么你将得到大量重复和冗余的代码,这是很繁琐且难以维护的,而这是一件不好的事情!

如果你可以编写格式化代码一次,并以与之前示例中添加内置的safe过滤器相同的方式将其添加到模板中的任何字段中,那不是很好吗?感谢Django的自定义模板标签和过滤器功能 (opens new window),你可以做到这一点。

自定义模板过滤器提供了一个接口,用于在模板中精确地操作上下文变量值的定义自己的Python函数 (opens new window)。 以与Django内置过滤器相同的方式定义自定义Markdown过滤器。

要定义自定义Markdown过滤器,首先在dmd_app/旁边的views.py和其他样板文件中创建一个新的templatetags/目录。确保在templatetags/内添加一个空的__init__.py文件,以便Django可以将其识别为新的Python包 (opens new window)。然后,在__init__.py旁边创建一个名为dmd_extras.py的新文件,并添加以下代码:

Python dmd_app/templatetags/dmd_extras.py

在此代码中,@register.filter使用名为registertemplate.Library实例将render_markdown()过滤器函数注册到项目的标签和过滤器库中。@stringfilter装饰器指示您的函数仅接受字符串值。mark_safe()函数的作用类似于safe内置过滤器,表示md.convert()方法生成的HTML字符可以安全地渲染,无需进一步转义。 请注意,在实际项目中,您需要仔细考虑您的具体用例,以确保您正在处理的文本确实是安全的。Django提供了一系列灵活的字符转义选项 (opens new window),用于在定义自定义标签和过滤器时使用。

现在,您已经将Markdown渲染代码移到了自定义过滤器函数中,可以从视图函数中删除与Markdown相关的代码:

您可以在模板代码中使用您的新render_markdown()过滤器:

HTML dmd_app/templates/dmd_app/markdown_content.html

现在,如果您重新加载http://127.0.0.1:8000/markdown-content/页面,您应该可以看到您的内容正常呈现为HTML,就像之前一样。但现在Django使用了您的自定义模板过滤器,您也可以在项目的管理界面中的任何其他位置使用它,以允许基于Markdown的编辑。

创建您自己的自定义Python-Markdo #

为了测试这个功能,您将创建一个扩展,可以通过slug而不是URL链接到网页。同时,使用标准的Markdown内联链接语法 (opens new window),即[链接文本](url)。在网页开发中,术语“slug”指的是URL的组成部分,由字母、数字、下划线或连字符组成,通常来自网页的标题或网站的某个部分。

您的自定义语法将允许作者编写[链接文本](slug:页面slug),其中slug指的是页面的slug。 page-slug 是在页面模型上定义的 slug 字段值。这将为内容创建者提供良好的用户体验改进,使他们无需为每个内部页面都输入完整的 URL。

Django 提供了 SlugField (opens new window) 模型字段,您可以将其添加到 MarkdownContent 模型定义中,并通过在控制台运行 python manage.py makemigrations,然后再运行 python manage.py migrate 来应用到项目的数据库中:

运行迁移命令后,编辑 dmd_app/admin.py,根据您在管理员表单中输入的 title 字段的值为您的 MarkdownContent.slug 字段创建一个值,如下所示:

现在,如果返回到管理员界面编辑 MarkdownContent 页面的内容,当您选择 Title 字段的表单字段时,您应该会看到新的 Slug 字段填充:

选择“保存”,然后编辑项目的urls.py文件,以在页面URL中使用MarkdownContent.slug字段:

Python dmd_project/urls.py

在这段代码中,您更新了path对象,以在URL字符串中使用新的slug参数。现在,如果您将浏览器指向http://127.0.0.1:8000/markdown-content/my-first-markdown-content/,那么Django将获取my-first-markdown-content的slug值,并将其作为参数传递给markdown_content_view()

但首先,您需要修改您的视图代码,以接受页面slug作为参数,并使用Django的get_object_or_404()函数查询页面或返回错误消息(如果URL中的slug不存在):

现在,为了在浏览器中导航到其中一个MarkdownContent页面,您需要一个 在URL的末尾添加特定页面的slug,如下所示:http://127.0.0.1:8000/markdown-content/my-first-markdown-content/

您现在可以构建一个自定义的Python-Markdown扩展,允许内容作者在其Markdown内容中使用您的新slug字段作为引用内部网站URL的更简洁的方式。首先,在您的dmd_app/目录中创建一个名为markdown_extensions.py的新文件。然后将以下代码添加到该文件中:

Python dmd_app/markdown_extensions.py

在这里,SlugFieldLinkInlineProcessor继承自LinkInlineProcessor,后者负责处理Markdown语法模式[链接文本](URL)中的内联链接。为了使用相同的语法模式链接到具有其slug的页面,您在子类中覆盖了.getLink()并检查使用您的自定义[链接文本](slug:页面slug)模式的内联链接。

如果内联链接以"slug,"开头,那么您将使用.reverse()来构建完整的相对URL。 将以下的Markdown翻译为中文并删除一级标题:nd return that as the href value. Then, you subclass markdown.Extension to replace LinkInlineProcessor with your custom extension.

Python-Markdown处理器按照特定的顺序运行,以正确地将Markdown语法转换为HTML代码。它们必须首先处理反引号和转义字符,然后处理链接,最后处理内联HTML。因为您的扩展添加了功能到现有的LinkInlineProcessor处理器,所以您希望使用与原始基类相同的优先级级别160

在创建自定义扩展时,您可以查看Python-Markdown的默认处理器顺序,方法是查看源代码 (opens new window)。此外,您还可以在官方的教程 (opens new window)中了解更多关于实现自定义Python-Markdown扩展的内容。

下一步是将您的自定义SlugFieldExtension添加到中。 现在重新登录管理员界面并创建一个新的 Markdown 内容页面记录:

Django Admin 新建内容 (opens new window)

在 Django 管理员界面创建一个新的 Markdown 内容实例

然后编辑原始 Markdown 内容页面的 "Content" 字段,使用自定义的 slug 字段 Markdown 语法 [链接文本](slug:page-slug),其中 page-slug 是你刚刚创建的新 Markdown 内容页面的 slug 字段值:

Django Markdown 自定义 Slug 语法 (opens new window)

编辑你的原始 Markdown 内容 使用您自定义的内联 slug 语法将以下 Markdown 翻译为中文并删除一级标题:

保存您的更改并导航到您的原始内容页面,您应该可以看到您的新内容呈现为内联 HTML 锚链接:

带有 Slug 链接的页面 (opens new window)

使用自定义 slug 语法呈现的 HTML 文本,以创建一个锚链接

点击链接,您的浏览器将使用您自定义的 Python-Markdown 扩展从页面 slug 中检索到的相对 URL 值导航到您的新的 Markdown 内容页面:

新的内容页面 (opens new window)

工作中的锚链接到您的第二个 Markdown 内容页面

通过在渲染过程的任何阶段中插入 Python-Markdown 代码的能力,有无数种方法来扩展其默认功能并自定义您自己的 Markdo wn集成与Django一起工作。 在这个小例子中,您修改了Markdown的内联链接语法,以与Django的slug字段和django.urls.reverse()一起工作,因此您现在对将这两种技术配对的潜在优势有了一定的了解。

将代码语法高亮添加到您的Markdown内容 #

拥有编写自己的Python-Markdown扩展的能力是很好的,但是就像软件开发中的所有事物一样,当有一个现成的解决方案可供满足您的需求时,最好不要重复造轮子。

为Markdown内容中的代码块添加语法高亮将是一个很好的功能,可以提高您网站用户的可读性。但是,将其作为自定义扩展来实现将是一项重大工程。幸运的是,Python-Markdown提供了一个官方支持的CodeHilite (opens new window)扩展,您可以将其插入到您的项目中。 使用我们的项目并免费使用。

要使用CodeHilite扩展,您首先必须安装Pygments (opens new window),这是一个用于Python的代码语法高亮的包:

安装了Pygments后,您可以使用其pygmentize命令行实用程序生成一个CSS样式表 (opens new window),以将语法高亮添加到您的代码块中。

但首先,您需要在dmd_app/内创建一个新的static/文件夹,以便Django知道在哪里找到您的静态CSS文件 (opens new window)。在该static/文件夹中,创建另一个名为dmd_app/的新文件夹,该文件夹的作用与之前模板文件夹结构相同。

更改目录到您的新静态文件夹,并运行以下pygmentize命令:

该命令告诉Pygments要执行的操作。 创建一个新的styles.css文件,使用Pygments的默认颜色设置和.codehilite CSS类名来为项目的HTML代码添加语法高亮。当Python-Markdown从Markdown渲染您的HTML时,CodeHilite扩展将在您的代码块中添加.codehilite CSS类名。

要在您的HTML中使用styles.css文件,您需要对模板代码进行以下修改:

HTML dmd_app/templates/dmd_app/markdown_content.html

此代码使用Django的static模板标签指向您的styles.css文件,以便您可以使用HTML <link>元素加载它。

在浏览器中导航到Django管理界面,并返回到您创建的第一个“Markdown内容”实例。将您在“内容”字段中输入的代码块更新为以下内容:

向Markdown代码块中添加python告诉CodeHilite要使用哪种编程语言进行语法高亮。您可以通过添加另一个代码块来进行测试。 将以下Markdown翻译成中文并删除第一级标题:将代码块添加到_Content_字段中,使用一些HTML代码进行高亮显示:

保存更改并打开dmd_extras.py,在其中定义了自定义的render_markdown()模板过滤器。更新render_markdown()以使用CodeHilite扩展:

Python dmd_app/templatetags/dmd_extras.py

因为CodeHilite是Python-Markdown的官方支持的扩展之一 (opens new window),你只需将"codehilite"字符串添加到你的extensions列表参数中,它就会起作用。

再次在浏览器中查看你的原始Markdown内容页面,你应该可以看到Python和HTML代码块的语法高亮正常工作:

带有语法高亮的代码块 (opens new window)

带有语法高亮的代码块

现在你的HTML内容包含了语法高亮。 语法高亮是一项很好的可读性增强功能,适用于您网站的用户。除了Python-Markdown的官方支持扩展 (opens new window)之外,还有许多第三方扩展 (opens new window)可用。

结论 #

在本教程中,您学习了如何利用Python-Markdown包在Django应用程序中呈现Markdown内容。该包提供了使用Markdown语法在Django管理界面中编写网站内容的能力。

您首先创建了一个单独的Django视图和模板,以HTML形式显示您的渲染内容。然后,您创建了一个自定义模板过滤器,以实现更灵活的方法。您可以在项目的其他模板中重用此过滤器。

通过创建一个使用Markdown的自定义Python-Markdown扩展,您发现了自定义Python-Markdown扩展的潜力。 使用行链接语法将网页slug转换为URL。然后,您开始通过使用CodeHilite扩展在网站内容中的代码块中添加语法高亮来探索官方和第三方Markdown扩展的生态系统。