日課書

编程100小时挑战

博客分页功能

当博客有很多文章后,在首页显示所有文章,会让加载速度变慢,同时也会影响浏览体验。一个好的办法,就是对博文进行分页,每页仅显示一部分文章,然后通过导航跳转到相应的页面。如果使用Flask,可以利用SQLAlchemy和Bootstrap插件的分页功能,高效的实现博客分页效果。

0. 创建虚拟文章数据

想要实现博客的分页功能,首先需要我们的博客有足够的数据,在开发阶段,只能通过自动产生数据的手段来满足要求。Python中的ForgeryPy包,可用于产生虚拟信息。

首先安装forgerypy,pip install forgerypy

为博客的文章Post模型,创建类方法,用来产生虚拟数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
summary = db.Column(db.String)
content = db.Column(db.Text)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))

@staticmethod
def generate_fake(count=100):
from random import seed, randint
import forgery_py

seed()
for i in range(count):
p = Post(title=forgery_py.lorem_ipsum.title(),
summary=forgery_py.lorem_ipsum.sentences(randint(1,2)),
content=forgery_py.lorem_ipsum.sentences(randint(5,8)),
timestamp=forgery_py.date.date(True),
author_id=User.query.filter_by(is_administrator=True).first().id
)
db.session.add(p)
db.session.commit()

1. 在页面中获得分页渲染数据

为了支持分页,我们在视图函数中需要获取到,分页类型的数据。可以利用SQLAlchemy提供的paginate()方法,,来显示某页记录。同时可以使用get方法来获取,通过查询字符串方式添加到URL的页码。

1
2
3
4
5
6
def index():
# ...
page = request.args.get('page', 1, type=int)
pagination = Post.query.order_by(Post.timestamp.desc()).paginate(page, per_page=current_app.config['GRITY_POSTS_PER_PAGE'], error_out=False)
posts = pagination.items
return render_template('index.html', posts=posts, pagination=pagination)

2. 添加分页导航

paginate()方法返回值是一个Pagination类对象,这个对象在SQLAlchemy中定义。其中很多属性可以用于在模板中生成分页链接,因此将其作为参数传入模板。在模板中利用Bootstrap的分页css类就能实现分页导航。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- app/templates/_macros.html -->
{% macro pagination_widget(pagination, endpoint) %}
<ul class="pagination">
<li{% if not pagination.has_prev %} class="disable"{% endif %}>
<a href="{% if pagination.has_prev %}{{ url_for(endpoint, page=pagination.page-1,
**kwargs) }}{% else %}#{% endif %}">
&laquo;</a>

</li>
{% for p in pagination.iter_pages() %}
{% if p %}
{% if p == pagination.page %}
<li class="active">
{% else %}
<li>
{% endif %}
<a href="{{ url_for(endpoint, page=p, **kwargs) }}">{{ p }}</a>
</li>
{% else %}
<li class="disable"><a href="#">&hellip;</a></li>
{% endif %}
{% endfor %}
<li{% if not pagination.has_next %} class="disable"{% endif %}>
<a href="{% if pagination.has_next %}{{ url_for(endpoint, page=pagination.page+1,
**kwargs) }}{% else %}#{% endif %}">
&rquo;</a>

</li>
</ul>
{% endmacro %}

把创建的宏引入页面

1
2
3
4
5
6
...
{% import "_macros.html" as macros %}
...
<div class="pagination">
{{ macros.pagination_widget(pagination,'.index') }}
</div>