大师网-带你快速走向大师之路 解决你在学习过程中的疑惑,带你快速进入大师之门。节省时间,提升效率

基于Flask的Markdown编辑器实践

Markdown编辑器——Editor.MD于Flask:

能预览的Markdown编辑器中, 读狗书时用的Flask-Pagedown比较不错, 在逛知乎的时候学习greyli大神的flask富文本编辑器实现,实践中遇到 Editor.md很合胃口, 类似简书或者 Remarkable的左右分栏预览方式非常喜欢,见坑就跳吧。

最终效果大概是这样的:

112218_7.png

Editor.md是个国人维护的开源项目, 四年没更新不过还是很好用的。相关Issues比较多,基本上小问题翻翻即可解决,

首先进入Editor.md官网下载zip,或者点击Github download下载

解压重命名为editormd,并置入static文件夹

表单类里使用Flask-WTF设置<textarea>标签存放文章内容, 定义TextAreaField字段, 对Body字段进行修改:

forms.py:

from wtforms import TextAreaField
#...

class PostForm(FlaskForm):
    #...
    body = TextAreaField('Body', [DataRequired()])
    #...

此时编辑模板的js脚本, 用于拾取id=editormd的 textarea 渲染的

这真的是一个坑大的决定,相关探究并无太多参考,Editor.MD已经荒废四年,相关文档都未必打得开,基本都是Java做后端,Flask上几乎没人用。相关讨论比较杂乱,甚至有的错漏百出。基本只能靠踩坑和阅读源码来运作。目前还在艰苦奋斗阶段,有能力后会编写相关拓展。

为便于支持更多的MarkDown格式甚至emoji代码高亮表格解析等问题。而不是一点点自定义,这次我们利用更强大的Editor.MD编辑自带的Markdown2HTML渲染方式。

new_post.html:

<link rel="stylesheet" href="{{ url_for('static',filename='editormd/css/editormd.css') }}"/>

这里我将Editor.MD置于static静态文件目录并重命名为editormd,个人根据目录自行更改即可。


使用saveHTMLToTextarea : true字段开启自动转换HTML为后台直接提取html文档提供接口。其中js代码处注意宽度设置与Bootstrap4的body相冲突,这里我们注释掉width字段,否则将无法直接提取html。如果是继承模板,引入js较多时,可以在js的顺序上优先保证editor.MD,上下文在最后继承

new_post.html:

<script src="{{ url_for('static',filename='editormd/examples/js/jquery.min.js') }}"></script>
<script src="{{ url_for('static',filename='editormd/editormd.min.js') }}"></script>
<script type="text/javascript">
    $(function () {
        editormd("fancy-editormd", {
            // width: "100%", 请不要添加
            height: 640,
            syncScrolling: "single",
            path: "{{ url_for('static',filename='editormd/lib/') }}",
            saveHTMLToTextarea : true
        });
    });
</script>
 {{ super() }}

结尾scripts段加载JS:顺序为jQuery在前, editormd.min.js在后.

相关的textarea部分 new_post.html:

<div id="fancy-editormd" class="editormd">
{{ form.body(style="display:none;") }}
</div>

在编辑文章的部分也照做即可。
其中的"fancy-editormd"字段是自定义的,用于拾取textarea
这里有个坑,如果使用WTForms渲染表单的话name属性是无法更改的,而editormd理论上是通过name=“fancy-editormd-markdown-doc"属性来渲染我们编辑markdown文件的地方
这里不用担心太多,如果没有多个textarea的话,其实我们只需要照常渲染formbody即可,再次渲染时可见Editor.MD会自动找到第一个textarea并渲染为markdown编辑器,并自动为我们生成了一个textareaname=”fancy-editormd-html-code"里面内容即是Editor.MD的HTML文件。

112218_1.png

这里我们将其保存下来,以便归档查看以及预览方便节省资源。如果正文渲染的话还是建议用markdown即时转换这样表格和特殊内容更加直观,当然如果服务器并发访问过多的话并不建议这么做。

首先在Forms中我们自定义一个body_html用于保存撰写文档时Editor.MD留下的HTML格式文档:
forms.py:

class PostForm(FlaskForm):
  #...
  body_html = HiddenField()

添加一个实例化数据库模型字段的Column类在文章模型中,注意如果使用Flask-Whooshee全文搜索的话建立索引字段改为我们的html格式文档,还好注意而后使用reindex()方法重建索引。(目前文档较少做了搜索也没用,而后会单独列出文章做详细讨论):

models.py:

@whooshee.register_model('title', 'body_html')
class Post(db.Model):
    #...
    body_html = db.Column(db.Text)

在文章管理蓝本中直接使用request来获取数据,不使用WTForms

admin.py:

from flask import request
# ...
@admin_bp.route('/post/new', methods=['GET', 'POST'])
@login_required
def new_post():
    if form.validate_on_submit():
        # ...
        body_html = request.form['fancy-editormd-html-code']
            post = Post(..., body_html = body_html)
            #...

同样在编辑文章的蓝本中也要做相应的修改:

@admin_bp.route('/post/<int:post_id>/edit', methods=['GET', 'POST'])
@login_required
def edit_post(post_id):
# ...
if form.validate_on_submit():
        # ...
        post.body_html = request.form['fancy-editormd-html-code']
        # ...
    # ...
form.body_html.data = post.body_html
# ...

此时我们要编辑的都编辑完了,在文档正部直接调用转换好的html即可,加个safe过滤器即可
{{ post.body_html| safe }}
当然代码高亮以及表格支持这些等等,想要显示出编辑时右边的预览的效果,还是需要调用Editor.MD的渲染,在文章正文的模板里做相应的修改:
引入静态文件,

    <link href="{{ url_for('static', filename='editormd/css/editormd.preview.min.css') }}" rel="stylesheet" />  
    <link href="{{ url_for('static', filename='editormd/css/editormd.css') }}" rel="stylesheet" /> 
    <!--以下是js部分 -->
    <script type="text/javascript" src="{{ url_for('static', filename='editormd/lib/marked.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='editormd/lib/prettify.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='editormd/editormd.min.js') }}"></script>
    <script type="text/javascript">
        editormd.markdownToHTML("fancy-content");
    </script>

在文档中定义我们自定义的fancy-content

<div class="content" id="fancy-content">
            {{ post.body_html| safe }}
</div>

这里无需使用post原文,无需定义textarea,直接用flask-WTF的定义表单即可,直接使用html文件即可。

没有引入Editor.MD的渲染表单:

112218_2.png

渲染后:


112218_3.png

再对比编辑栏效果:

112218.png

在文章预览处我们做相应的修改,
_post.html :

<h3 class=`text-primary`><a href=`{{ url_for('.show_post', post_id=post.id) }}`>{{ post.title }}</a></h3>
    <p>
        <div class=`post-body`>
            {% if post.body_html %}
                {{ post.body_html |safe|striptags|truncate }}
            {% else %}
                {{ post.body }}
            {% endif %}
        </div>
        <small><a href=`{{ url_for('.show_post', post_id=post.id) }}`>Read More</a></small>
    </p>

先调用safe转义过滤器, 再调用striptags清除html渲染后的格式, 最后调用truncate过滤器只保留开头255个字符,而后惯例一个Read More链接.

如果使用了Flask-Migrate的Alembic话,创建迁移环境生成迁移脚本更新数据库三部曲即可。由于数据库已经从SQlite迁移到了MySQL, 一切都没什么。生产环境还是先备份以下


目前来说Markdown于Flask的实现应该是打好了基础. Editor.md的界面比较讨喜, 相比其他富文本编辑器更加受青睐. 关于Editor.md的进阶功能还有许多, 日后需要还会继续添加.至此,Editor.MD的回调已经完成了。在下篇里我会谈一下图片上传的问题。

个人是新入门的求职学生,欢迎大家指教