将markdown渲染成html
Markdown
是一种非常简洁的文本语言,很多程序员和技术工作者都用它来写博文和说明文档,想要将markdown
渲染成html
可以使用Python
得markdown
包。
pip install markdown
在渲染页面的视图函数中,将文章主题利用markdown渲染
def detail(request, id):
post = Post.objects.get(id=id)
post.body = markdown.markdown(post.body,
extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
'markdown.extensions.toc',
])
return render(request, 'blog/detail.html', {'post': post})
在前端页面上通过safe
过滤器就能将其渲染成html
代码,{{post.body | safe}}
markdown 目录
在页面的任何地方插入目录
上述方式的一个局限局限性就是只能通过 [TOC] 标记在文章内容中插入目录。如果我想在页面的其它地方,比如侧边栏插入一个目录该怎么做呢?方法其实也很简单,只需要稍微改动一下解析 Markdown 文本内容的方式即可,具体代码就像这样:
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)
post.toc = md.toc
return render(request, 'blog/detail.html', context={'post': post})
markdown.markdown()
方法来渲染 post.body 中的内容,而是先实例化了一个 markdown.Markdown
对象 md
,和 markdown.markdown()
方法一样,也传入了extensions
参数。接着我们便使用该实例的 convert
方法将 post.body
中的 Markdown
文本解析成 HTML
文本。而一旦调用该方法后,实例 md
就会多出一个 toc
属性,这个属性的值就是内容的目录,我们把 md.toc
的值赋给 post.toc
属性(要注意这个 post 实例本身是没有 toc
属性的,我们给它动态添加了 toc
属性,这就是 Python
动态语言的好处)。
接下来就在博客文章详情页的文章目录侧边栏渲染文章的目录吧!删掉占位用的目录内容,替换成如下代码:
{% block toc %}
<div class="widget widget-content">
<h3 class="widget-title">文章目录</h3>
{{ post.toc|safe }}
</div>
{% endblock toc %}
即使用模板变量标签 {{ post.toc }}
显示模板变量的值,注意 post.toc
实际是一段 HTML
代码,我们知道 django
会对模板中的 HTML 代码进行转义,所以要使用 safe 标签防止 django 对其转义。其最终渲染后的效果就是:
美化标题
文章内容的标题被设置了锚点,点击目录中的某个标题,页面就会跳到该文章内容中标题所在的位置,这时候浏览器的 URL 显示的值可能不太美观,比如像下面的样子:
http://127.0.0.1:8000/posts/8/#_1
http://127.0.0.1:8000/posts/8/#_3
_1 就是锚点,Markdown 在设置锚点时利用的是标题的值,由于通常我们的标题都是中文,Markdown 没法处理,所以它就忽略的标题的值,而是简单地在后面加了个 _1 这样的锚点值。为了解决这一个问题,需要修改一下传给 extentions 的参数,其具体做法如下:
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
TocExtension(slugify=slugify),
])
post.body = md.convert(post.body)
m = re.search(r'<div class="toc">\s*<ul>(.*)</ul>\s*</div>', md.toc, re.S)
post.toc = m.group(1) if m is not None else ''
return render(request, 'blog/detail.html', context={'post': post})
和之前不同的是,extensions 中的 toc 拓展不再是字符串 markdown.extensions.toc ,而是 TocExtension 的实例。TocExtension 在实例化时其 slugify 参数可以接受一个函数,这个函数将被用于处理标题的锚点值。Markdown 内置的处理方法不能处理中文标题,所以我们使用了 django.utils.text 中的 slugify 方法,该方法可以很好地处理中文。
这时候标题的锚点 URL 变得好看多了。
http://127.0.0.1:8000/posts/8/#我是标题一
http://127.0.0.1:8000/posts/8/#我是标题二下的子标题