Django 博客使用 Markdown 自动生成文章目录

我们的 Django 博客使用了 Markdown 来为文章提供排版支持。Markdown 在渲染内容的同时还可以自动提取整个内容的目录结构,本文将教你如何使用 Markdown 来为文章自动生成目录。

在文中插入目录

假设我们 Django 博客的文章模型如下:

from django.db import models

class Post(models.Model):
    # Other fields ...
    body = models.TextField()

body 是我们存储 Markdown 文本的字段。

假设访问文章的内容页面由 detail 视图函数处理,我们在 detail 视图函数中将 body 字段中的 Markdown 文本渲染成 HTML 文本。其过程如下:

import markdown
from django.shortcuts import render, get_object_or_404

def detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    post.body = markdown.markdown(post.body,
                                  extensions=[
                                      'markdown.extensions.extra',
                                      'markdown.extensions.codehilite',
                                      'markdown.extensions.toc',
                                  ])
    return render(request, 'blog/detail.html', {'post': post})

markdown.markdown() 方法把 post.body 中的 Markdown 文本渲染成了 HTML 文本。同时我们还给该方法提供了一个 extensions 的额外参数。其中 markdown.extensions.codehilite 是代码高亮拓展,而 markdown.extensions.toc 就是自动生成目录的拓展。

在渲染 Markdown 文本时加入了 toc 拓展后,就可以在文中插入目录了。方法是在书写 Markdown 文本时,在你想生成目录的地方插入 [TOC] 标记即可。

例如新写一篇 Markdown 博文,其 Markdown 文本内容如下:

[TOC]

## 我是标题一

这是标题一下的正文

## 我是标题二

这是标题二下的正文

### 我是标题二下的子标题
这是标题二下的子标题的正文

## 我是标题三
这是标题三下的正文

其最终渲染后的效果就是:

原本 [TOC] 标记的地方被内容的目录替换了。

在页面的任何地方插入目录

上述方式的一个局限局限性就是只能通过 [TOC] 标记在文章内容中插入目录。如果我想在页面的其它地方,比如侧边栏插入一个目录该怎么做呢?

方法其实也很简单,只需要稍微改动一下渲染 Markdown 文本内容的方式即可,具体代码就像这样:

import markdown
from django.shortcuts import render, get_object_or_404

def detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    md = markdown.Markdown(extensions=[
        'markdown.extensions.extra',
        'markdown.extensions.codehilite',
        'markdown.extensions.toc',
    ])
    post.body = md.convert(post.body)
    return render(request, 'blog/detail.html', {'post': post, 'toc': md.toc})

和上一节不同,我们没有直接用 markdown.markdown() 方法来渲染 post.body 中的内容,而是先实例化了一个 markdown.Markdownmd,和 markdown.markdown() 方法一样,也传入了 extensions 参数。

接着我们便使用该实例的 convert 方法将 post.body 中的 Markdown 文本渲染成 HTML 文本。而一旦调用该方法后,实例 md 就会多出一个 toc 属性,这个属性的值就是内容的目录,我们把 md.toc 作为模板变量传给了模板后,就可以在模板中使用了。

例如我想在页面侧边栏显示目录(目录已经保存在模板变量 toc 中),只需在模板中引用这个变量即可:

<aside>
  <!-- 由于 toc 的值为一段 HTML 文本,所以要使用 safe 标签过滤 -->
  {{ toc|safe }}
</aside>

其最终渲染后的效果就是:

可以在侧边栏看到内容 的目录了。

美化标题的锚点 URL

文章内容的标题被设置了锚点,点击目录中的某个标题,页面就会跳到该文章内容中标题所在的位置,这时候浏览器的 URL 显示的值可能不太美观,比如像下面的样子:

http://127.0.0.1:8000/post/8/#_1 http://127.0.0.1:8000/post/8/#_3

#_1 就是锚点,Markdown 在设置锚点时利用的是标题的值,由于通常我们的标题都是中文,Markdown 没法处理,所以它就忽略的标题的值,而是简单地在后面加了个 _1 这样的锚点值。

为了解决这一个问题,我们需要修改一下传给 extentions 的参数,其具体做法如下:

import markdown
from django.shortcuts import render, get_object_or_404

from django.utils.text import slugify
from markdown.extensions.toc import TocExtension

def detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    md = markdown.Markdown(extensions=[
        'markdown.extensions.extra',
        'markdown.extensions.codehilite',
        TocExtension(slugify=slugify),
    ])
    post.body = md.convert(post.body)
    return render(request, 'blog/detail.html', {'post': post, 'toc': md.toc})

和上一节不同的是,extensions 中的 toc 拓展不再是字符串 markdown.extensions.toc ,而是 TocExtension 的实例。

TocExtension 在实例化时其 slugify 参数可以接受一个函数作为参数,这个函数将被用于处理标题的锚点值。Markdown 内置的处理方法不能处理中文标题,所以我们使用了 django.utils.text 中的 slugify 方法,该方法可以很好地处理中文。

这时候标题的锚点 URL 变得好看多了。

http://127.0.0.1:8000/post/8/#我是标题一 http://127.0.0.1:8000/post/8/#我是标题二下的子标题

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏葡萄城控件技术团队

Spread for Windows Forms高级主题(4)---自定义用户交互

你可以从多方面自定义用户界面来自定义用户与Spread控件的交互方式。同时,你还可以自定义如何处理用户交互方式。 设置允许用户进行的操作 下面的列表总结了通过控...

22060
来自专栏hrscy

iOS 9 Storyboard 教程(二上)介绍Segue静态单元格(static cell)

Add Player 最终的设计看上去像下面这样:#接第一部分: 原帖地址 简书地址

30710
来自专栏Windows Community

Extensions in UWP Community Toolkit - FrameworkElement Extensions

概述 UWP Community Toolkit Extensions 中有一个为FrameworkElement 提供的扩展 - FrameworkEleme...

38080
来自专栏技术墨客

React新特性——Protals与Error Boundaries

在React 16.x 新增了一个名为“Protals”的特性,直接按照字面意思翻译实在不靠谱。在描述这个特性时,我们还是用官方的英文单词来指定它。Portal...

29240
来自专栏小狼的世界

Vimperator:玩酷你的Firefox

First there was a Navigator, then there was an Explorer. Later it was time for a...

13540
来自专栏happyJared

IDEA快捷键拆解系列(四):View篇

  以下是关于View导航项及其每一子项的拆解介绍,其中,加粗部分的选项是博主认为比较重要的。

14510
来自专栏Django中文社区

Markdown 自动生成文章目录

我们的之前在博客中使用了 Markdown 来为文章提供排版支持。Markdown 在渲染内容的同时还可以自动提取整个内容的目录结构,现在我们来使用 Markd...

75260
来自专栏iOS开发随笔

React Native 第一篇-Hello World!

14230
来自专栏Hongten

pygame系列_font游戏字体_源码下载

如果定义好了字体,那么我们应该把字体显示到suiface上面去,我们应该这样操作:

14520
来自专栏木宛城主

ASP.NET MVC使用Bootstrap系列(2)——使用Bootstrap CSS和HTML元素

Bootstrap提供了一套丰富CSS设置、HTML元素以及高级的栅格系统来帮助开发人员快速布局网页。所有的CSS样式和HTML元素与移动设备优先的流式栅格系统...

1.2K80

扫码关注云+社区

领取腾讯云代金券