我们的 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.Markdown
类 md
,和 markdown.markdown()
方法一样,也传入了 extensions
参数。
接着我们便使用该实例的 convert
方法将 post.body
中的 Markdown 文本渲染成 HTML 文本。而一旦调用该方法后,实例 md
就会多出一个 toc
属性,这个属性的值就是内容的目录,我们把 md.toc
作为模板变量传给了模板后,就可以在模板中使用了。
例如我想在页面侧边栏显示目录(目录已经保存在模板变量 toc
中),只需在模板中引用这个变量即可:
<aside>
<!-- 由于 toc 的值为一段 HTML 文本,所以要使用 safe 标签过滤 -->
{{ toc|safe }}
</aside>
其最终渲染后的效果就是:
可以在侧边栏看到内容 的目录了。
美化标题的锚点 URL
文章内容的标题被设置了锚点,点击目录中的某个标题,页面就会跳到该文章内容中标题所在的位置,这时候浏览器的 URL 显示的值可能不太美观,比如像下面的样子:
#_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 变得好看多了。
-- EOF --
帮到了,谢谢!
博主你好,我按照上面的,引入了'markdown.extensions.toc',也使用了convert()方法,但是在传入变量md.toc的时候,pycharm提示unresolved attribute for 'toc' for class 'Markdown',目录生成不出来,请问是什么问题?
这个不用管pycharm的提示,要在正文中使用 [toc] 标签才能产生目录,请看博客文章中的讲解
这个TocExtension(slugify=slugify)怎么想到的,厉害啊。