Markdown 语法和代码高亮_生成目录
为了增加用户浏览文章的便捷 修改文章界面左侧sidebar为文章目录栏。
让博客支持 Markdown 语法和代码高亮
为了让博客文章具有良好的排版,显示更加丰富的格式,我们使用 Markdown 语法来书写博文。Markdown 是一种 HTML 文本标记语言,只要遵循它约定的语法格式,Markdown 的解析工具就能够把 Markdown 文档转换为标准的 HTML 文档,从而使文章呈现更加丰富的格式,例如标题、列表、代码块等等 HTML 元素。由于 Markdown 语法简单直观,不用超过 5 分钟就可以轻松掌握常用的标记语法,因此大家青睐使用 Markdown 书写 HTML 文档。下面让我们的博客也支持使用 Markdown 写作。
安装 Python Markdown
将 Markdown 格式的文本解析成标准的 HTML 文档是一个复杂的工程,好在已有好心人帮我们完成了这些工作,直接拿来使用即可。首先安装 Markdown,这是一个 Python 第三方库,在项目根目录下运行命令
pip install markdown
在modle.py中我们增加函数。对文章makrodwn内容进行转换再返回:
# 文章
class Article(models.Model):
······
def body_to_markdown(self):
md_content = emoji.emojize(self.body, use_aliases=True)
return markdown.markdown(md_content, extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
# 'markdown.extensions.toc',
TocExtension(slugify=sfy),
])
前端中我们可以调用模板函数获取值:
<!-- 文章内容 -->
<div class=" f-17" >
{{ article.body_to_markdown |safe }}
<p class="font-weight-bold text-info">
<i class="fa fa-bullhorn mx-1"></i>
原创文章,转载请注明出处:{{ request.build_absolute_uri }}
</p>
</div>
safe 标签
我们在发布的文章详情页没有看到预期的效果,而是类似于一堆乱码一样的 HTML 标签,这些标签本应该在浏览器显示它自身的格式,但是 django 出于安全方面的考虑,任何的 HTML 代码在 django 的模板中都会被转义(即显示原始的 HTML 代码,而不是经浏览器渲染后的格式)。为了解除转义,只需在模板变量后使用 safe 过滤器即可,告诉 django,这段文本是安全的,你什么也不用做。在模板中找到展示博客文章内容的 {{ post.body }} 部分,为其加上 safe 过滤器:{{ post.body|safe }},大功告成,这下看到预期效果了。
Markdown 测试
safe 是 django 模板系统中的过滤器(Filter),可以简单地把它看成是一种函数,其作用是作用于模板变量,将模板变量的值变为经过滤器处理过后的值。例如这里 {{ post.body|safe }},本来 {{ post.body }}经模板系统渲染后应该显示 body 本身的值,但是在后面加上 safe 过滤器后,渲染的值不再是 body 本身的值,而是由 safe 函数处理后返回的值。过滤器的用法是在模板变量后加一个 | 管道符号,再加上过滤器的名称。可以连续使用多个过滤器,例如 {{ var|filter1|filter2 }}。
代码高亮,使用js文件解析和利用github代码高亮文件
程序员写博客免不了要插入一些代码,Markdown 的语法使我们容易地书写代码块,但是目前来说,显示的代码块里的代码没有任何颜色,很不美观,也难以阅读,要是能够像代码编辑器里一样让代码高亮就好了。
代码高亮我们借助 js 插件来实现,其原理就是 js 解析整个 html 页面,然后找到代码块元素,为代码块中的元素添加样式。我们使用的插件叫做 highlight.js 和 highlightjs-line-numbers.js,前者提供基础的代码高亮,后者为代码块添加行号。
首先在 base.html 的 head 标签里引入代码高亮的样式,有多种样式供你选择,这里我们选择 Github 主题的样式。样式文件直接通过 CDN 引入,同时在 style 标签里自定义了一点元素样式,使得代码块的显示效果更加完美。
<link href="https://cdn.bootcss.com/highlight.js/9.15.8/styles/github.min.css" rel="stylesheet">
<style type="text/css">
.codehilite {
padding: 0;
}
/* for block of numbers */
.hljs-ln-numbers {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
text-align: center;
color: #ccc;
border-right: 1px solid #CCC;
vertical-align: top;
padding-right: 5px;
}
.hljs-ln-n {
width: 30px;
}
/* for block of code */
.hljs-ln .hljs-ln-code {
padding-left: 10px;
white-space: pre;
}
.markdown-body img {
max-width: 90% !important;
width: auto;
height: auto;
text-align: center;
margin-left: auto;
margin-right: auto;
display: block;
padding: 10px 0px 10px 0px;
}
.freen-sidebar-wrapper:first-child{
margin-top: 60px;
}
.freen-menu-ul > li > ul > li a {
padding-right: 1rem;
}
</style>
并为文章的最顶层div增加class="markdown-body"
元素,好让css文件起作用。
使用pygments代码高亮
安装:
(env) PS F:\workspace\django_blog> pip install pygments env) PS F:\workspace\django_blog> pip install pygments
引入github对markdown进行修饰的css文件:
<link href="https://cdn.bootcss.com/github-markdown-css/3.0.1/github-markdown.min.css" rel="stylesheet">
和之前的不一样。因为我们只需要支持除代码之外的样式
可以在官网中看看pygments的使用:https://pygments.org/demo/
使用方法进入到static/css文件夹中
pygmentize -f html -a .codehilite -S default > pygments.css
-a .highlight指所有css选择器都具有.highlight这一祖先选择器
-S default就是指定所需要的样式了,各位可以对各种样式都尝试一下。在官网上是可以直接尝试的哦!
pygments.css将内容输出到pygments.css文件中
生成我们需要的css文件,并引用:
<!-- 文本区 -->
<link rel="stylesheet" href="{% static 'css/pygments.css' %}">
用这种方式我们尝试几款代码css样式。
第一种default:
第二种monokai:
manokai的样式和我们上面引入的gihub的样式发生了冲突,我们需要调整以下参数:
<style type="text/css">
.markdown-body img {
max-width: 90% !important;
width: auto;
text-align: center;
margin-left: auto;
margin-right: auto;
display: block;
padding: 10px 0px 10px 0px;
}
.codehilite pre{ color: #f8f8f2 }
.codehilite .err {
color: #f8f8f2;
background: #272822;
}
.markdown-body .highlight pre, .markdown-body pre {
background: #272822;
}
.freen-sidebar-wrapper:first-child{
margin-top: 60px;
}
.freen-menu-ul > li > ul > li a {
padding-right: 1rem;
}
</style>
<!-- 文本区 -->
<link rel="stylesheet" href="{% static 'css/monokai.css' %}">
在base中添加block,方便导入目录
在base.html中修改如下:
<!--freen sidebar -->
{% cache 7200 'sidebar' %}
<aside id="freen-sidebar" class="freen-sidebar">
{% block md_toc %}
···html代码
{% endblock %}
</aside>
{% endcache %}
在合适的地方添加md_toc模块,方便article界面进行继承。
{% block md_toc %}
文章目录
{% endblock %}
效果如下:
在model中添加函数,获取我们的目录:
def body_to_toc(self):
md = markdown.Markdown(extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
'markdown.extensions.toc',
])
md.convert(self.body)
m = re.search(r'<div class="toc">\s*<ul>(.*)</ul>\s*</div>', md.toc, re.S)
toc = m.group(1) if m is not None else ''
return toc
设置前端样式,让其变得更美观
{% block md_toc %}
<div id="freen-sidebar-cell" class="freen-sidebar-cell w-100">
<!-- MENU -->
<nav id="freen-main-menu" class="menu-main-menu-container freen-main-menu">
<ul class="freen-menu-ul">
<li><a href="#">⛳文章目录</a>
{{ article.body_to_toc |safe }}
</li>
</ul>
</nav>
</div>
{% endblock %}
稍微修一下前端样式:
.freen-sidebar-wrapper:first-child{
margin-top: 60px;
}
.freen-menu-ul > li > ul > li a {
padding-right: 1rem;
}
大致效果如下:
slugify文章目录
文章内容的标题被设置了锚点,点击目录中的某个标题,页面就会跳到该文章内容中标题所在的位置,这时候浏览器的 URL 显示的值可能不太美观,比如像下面的样子:
http://127.0.0.1:8000/article/8/#_1
http://127.0.0.1:8000/article/8/#_3
#_1
就是锚点,Markdown 在设置锚点时利用的是标题的值,由于通常我们的标题都是中文,Markdown 没法处理,所以它就忽略的标题的值,而是简单地在后面加了个 _1 这样的锚点值。为了解决这一个问题,需要修改一下传给 extentions 的参数,其具体做法如下:
model.py:
记得import:
from markdown.extensions.toc import TocExtension
from django.utils.text import slugify as sfy
def body_to_markdown(self):
md_content = emoji.emojize(self.body, use_aliases=True)
return markdown.markdown(md_content, extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
# 'markdown.extensions.toc',
TocExtension(slugify=sfy),
])
def body_to_toc(self):
md = markdown.Markdown(extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
# 'markdown.extensions.toc',
# 记得在顶部引入 TocExtension 和 slugify
TocExtension(slugify=sfy),
])
md.convert(self.body)
m = re.search(r'<div class="toc">\s*<ul>(.*)</ul>\s*</div>', md.toc, re.S)
toc = m.group(1) if m is not None else ''
return toc
和之前不同的是,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/#我是标题二下的子标题
本作品采用《CC 协议》,转载必须注明作者和本文链接