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 协议》,转载必须注明作者和本文链接
文章!!首发于我的博客Stray_Camel(^U^)ノ~YO
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!