Flask实战4-博客文章

本实战内容包括: 分页,markdown支持

分页

利用Flask-SQLAlchemy提供的paginate方法:

page页数是唯一需求的参数,error_out=True如果页数超出了范围则返回404

# ...
pagination = query.order_by(Post.timestamp.desc()).paginate(    # paginate是Flask-SQLAlchemy提供的分页方法
    page, per_page=current_app.config['FLASKR_POSTS_PER_PAGE'], error_out=False
)

paginate()方法的返回值是一个Pagination类对象,这个类在Flask-SQLAlchemy中定义。这个对象包含很多属性,用于在模版中生成分页链接,因此将其作为参数传入模版,分页对象属性简介:

属性 说明
items 当前页面中的记录
query 分页的源查询
page 当前页数
prev_num 上一页页数
next_num 下一页页数
has_next 如果有下一页,返回True
has_prev 如果有上一页,返回True
pages 总页数
per_page 每页显示的记录数
total 查询返回的记录总数
prev() 上一页分页对象
next() 下一页分页对象

配合Bootstrap的分页CSS,实现分页模版宏:

其中最关键的就是分页对象的iter_pages()方法

iter_pages()返回一个迭代器,返回一个在分页中显示页数的列表。这个这个列表最左边显示left_edge页,最右边显示right_edge页 当前页面左边显示left_current页,当前页面右边显示right_current页;例如,在一个100页的列表中。当前页面第50页,使用默认配置,该方法返回值如下:1, 2, None, 48, 49, 50, 51, 52, 53, 54, 55, None, 99, 100 (None表示页数之间的间隔)

iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2)
{% macro pagination_widget(pagination, endpoint, fragment='') %}
    {# 没有前一页,添加class=disble,不可点击,a href=##} « {% for p in pagination.iter_pages() %} {% if p %} {% if p == pagination.page %}
  • {{ p }}
  • {% else %}
  • {{ p }}
  • {% endif %} {% else %}
  • {% endif %} {% endfor %} {# 没有后一页,添加class=disble,不可点击,a href=##} »
{% endmacro %}

在其他默认里使用分页宏

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% import "_macros.html" as macros %}


MarkDown

实现这个功能需要用到一些包:

  • PageDown: 使用JavaScript实现的客户端Markdown到HTML的转换
  • Flask-PageDown: 为Flask包转的PageDown, 把PageDown集成到Flask-WTF的表单中
  • MarkDown: 使用python实现服务器端markdown到html的转换
  • Bleach: 使用python实现的HTML清理器

使用Flask-PageDown

Flask-PageDown扩展定义了一个PageDownField类,这个类和WTForms中的TextAreaField接口一致。使用PageDownField之前。先要初始化扩展:

from flask_pagedown import PageDown

pagedown = PageDown()
pagedown.init_app(app)

将表单模型中的TextAreaField类型替换为PageDownFiled类型

from flask_pagedown.fields import PageDownField

class PostForm(Form):
    # body = TextAreaField("What's on your mind?", validators=[DataRequired()])
    body = PageDownField("What's on your mind?", validators=[DataRequired()])
    submit = SubmitField('Submit')

在模版中添加pagedown的js

{% block scripts %}
    {{ super() }}
    {{ pagedown.include_pagedown() }}
{% endblock %}

在服务器上处理markdown到html并存入DB

先使用python markdown将markdown转换成html,在使用bleach清理,确保只保存只允许的html标签,在数据模型中添加转换方法:

from markdown import markdown
import bleach


class Poset(db.Model):
    # ...
    body_html = db.Column(db.Text)
    
    @staticmethod
    def on_changed_body(target, value, oldvalue, initiator):
        allowed_tags = [
            'a', 'abbr', 'acronym', 'b', 'blockquote', 'code',
            'em', 'i', 'li', 'ol', 'pre', 'strong', 'ul',
            'h1', 'h2', 'h3', 'p'
        ]
        target.body_html = bleach.linkify(bleach.clean(markdown(value, output_format='html'), tags=allowed_tags, strip=True))

# on_changed_body 函数注册在 body 字段上,是 SQLAlchemy“set”事件的监听程序,这意 味着只要这个类实例的 body 字段设了新值,函数就会自动被调用。

db.event.listen(Post.body, 'set', Post.on_changed_body)

在模版中显示

{% if post.body_html %}
    {{ post.body_html | safe }}  # 告诉jinja不转移html
{% else %}
    {{ post.body }}
{% endif %}

你可能感兴趣的:(Flask实战4-博客文章)