日課書

编程100小时挑战

在博客中支持Markdown

Markdown是一种轻量级的「标记语言」,它允许作者使用简单的标签控制文章的版式,同时不降低作为纯文本的可读性。而且利用各种方式可以很方便的把它转换成排版精美的HTML页面。所以Markdown非常适合写博客。

在博客中我们希望,输入文章的文本输入支持Markdown语法,同时要有实时预览的功能。如果是上传的Markdwon文本,也能有转换成相应的HTML。

0. 用到的功能包

  • PageDown:用JavaScript实现的客户端Markdown到HTML转换程序。
  • Flask-PageDown:为Flask包装的PageDown,把PageDown集成到Flask-WTF表单中。
  • Markdown: 使用Python实现的服务器端Markdown到HTML的转换程序。
  • Bleach:使用Python实现的HTML过滤器。

安装:$ pip install flask-pagedown markdown bleach

1. 使用Flask-PageDown

初始化Flask-PageDown

1
2
3
4
5
6
7
8
from flask_pagedown import PageDown
# ...
pagedown = PageDown()
# ...
def create_app(config_name):
# ...
pagedown.init_app(app)
# ...

把WTF表单中的多行文本控件,修改为PageDownField

1
2
3
4
5
from flask_pagedown.fields import PageDownField

class PostForm(Form):
content = PageDownField("What's on your mind?", validators=[Required()])
# ...

加载Javas脚本

1
2
3
4
{% block scripts %}
{{ super() }}
{{ pagedown.include_pagedown() }}
{% endblock %}

2. 在服务器上处理Markdown

在表单提交后,之前的预览格式就会消失,上传到服务器的只有Markdown文本。我们可以在需要显示文章时,在页面渲染Markdown,但如果每次都这么做,效率很低。因此我们在服务器端把Markdown转换成HTML代码,使用Bleach留下需要的标签,保存到数据库中。当需要显示文章时,就直接读取相应的HTML代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from markdown import markdown
import bleach

class Post(db.Model):
# ...
content_html = db.Column(db.Text)

# ...
@staticmethod
def on_changed_content(target, value, oldvalue, initiator):
allowed_tags = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em',
'i', 'li', 'ol', 'pre', 'strong', 'ul', 'h1', 'h2', 'h3', 'h4', 'p']
target.content_html = bleach.linify(bleach.clean(markdown(value,
output_format='html'), tags=allowed_tags, strip=True))

db.event.listen(Post.content, 'set', Post.on_changed_content)

在模板中使用文章美容的HTML格式

1
2
3
4
5
6
7
<div class="post-content">
{% if post.content_html %}
{{ post.content_html | safe }}
{% else %}
{{ post.content }}
{% endif %}
<div>

3. 博客文章固定连接

每篇博客文章都应该有一个专门的页面,固定的链接,方便在网上分享传播。这个URL可以是文章ID或者更有意义、可读性高的字符串。

1
2
3
4
@main.route('/post/<int:id>')
def post(id):
post = Post.query.get_or_404(id)
return render_template('post.html', posts=[post])

在html模板中可以使用如下方法,为博客文章指定固定链接

1
<a href="{{ url_for('.post', id=post.id) }}">Permalink</a>

4. 博客文章编辑器

博客登录后,管理员可以对文章进行编辑。编辑文章需要单独的页面。这个页面会有表单显示原来文章的相关信息,可以在这上面直接修改,同时下面会有文章预览,提交后修改完成。

编辑博客文章的路由如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@admin_required
@main.route('/edit/<int:id>', methods=['GET', 'POST'])
def edit(id):
post = Post.query.get_or_404(id)
form = PostForm()
if form.validate_on_submit():
post.title = form.title.data
post.summary = form.summary.data
post.content = form.content.data
db.session.add(post)
flash('The post has been updated.')
return redirect(url_for('.post', id=post.id))
form.title.data = post.title
form.summary.data = post.summary
form.content.data = post.content
return render_template('edit_post.html', form=form)

编辑博客文章的模板和创建文章基本相同。之后再需要进入编辑的地方添加如下链接

1
2
<a href="{{ url_for('.edit', id=post.id) }}"><span class="label label-danger">
Edit</span></a>