Flask-Alchemy的paginate()查询方法支持分页。比如说,我想要获取用户第一组20个的关注帖子,我可以将语句最后的all()替换为:
>>> user.followed_posts().paginate(1, 20, False).items
paginate方法可以被Flask-SQLAlchemy的任何查询对象调用。它接收三个参数:
paginate的返回值是一个Pagination对象。该对象的items属性包含请求页面的条目列表。
现在来实现index()视图函数的分页功能。首先,向应用添加一个配置项,用于确定每页显示多少个条目。
config.py
class Config(object):
# ...
POSTS_PER_PAGE = 3
将页码并入应用URL的常用方法是使用查询字符串参数来指定可选的页码,如果该参数没有给出则默认为1。下面是一些示例URL:
要获取查询字符串的参数,可以使用Flask的request.args对象。
app/routes.py
@app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
@login_required
def index():
# ...
page = request.args.get('page', 1, type=int)
posts = current_user.followed_posts().paginate(
page, app.config['POSTS_PER_PAGE'], False)
return render_template('index.html', title='Home', form=form,
posts=posts.items)
@app.route('/explore')
@login_required
def explore():
page = request.args.get('page', 1, type=int)
posts = Post.query.order_by(Post.timestamp.desc()).paginate(
page, app.config['POSTS_PER_PAGE'], False)
return render_template("index.html", title='Explore', posts=posts.items)
通过这些更改,这两个路由就能确定要显示的页码了,是page查询字符串的参数或是默认值1,通过paginate()方法检索特定页面的结果。POSTS_PER_PAGE配置项则通过app.config对象确定了页面的条目数量。
下一个更改是在博客帖子列表的底部添加链接,使用户可以切换到下一页/上一页。前面提到,paginate()的返回值是Flask-SQLAlchemy的Pagination类的对象,除了上面用到的items属性之外,该对象还有其他几个对于创建分页对象有用的属性:
有了这四个元素,我就可以生成下一页和上一页的链接并传递给模板来渲染了:
app/routes.py
@app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
@login_required
def index():
# ...
page = request.args.get('page', 1, type=int)
posts = current_user.followed_posts().paginate(
page, app.config['POSTS_PER_PAGE'], False)
next_url = url_for('index', page=posts.next_num) \
if posts.has_next else None
prev_url = url_for('index', page=posts.prev_num) \
if posts.has_prev else None
return render_template('index.html', title='Home', form=form,
posts=posts.items, next_url=next_url,
prev_url=prev_url)
@app.route('/explore')
@login_required
def explore():
page = request.args.get('page', 1, type=int)
posts = Post.query.order_by(Post.timestamp.desc()).paginate(
page, app.config['POSTS_PER_PAGE'], False)
next_url = url_for('explore', page=posts.next_num) \
if posts.has_next else None
prev_url = url_for('explore', page=posts.prev_num) \
if posts.has_prev else None
return render_template("index.html", title='Explore', posts=posts.items,
next_url=next_url, prev_url=prev_url)
这两个视图函数中的next_url和prev_url只有在有效时才会被设为url_for()返回的url。如果当前页是第一页或最后一页,则Pagination对象的has_prev或has_next属性会是False。
修改模板:
app/templates/index.html
...
{% for post in posts %}
{% include '_post.html' %}
{% endfor %}
{% if prev_url %}
<a href="{{ prev_url }}">Newer posts</a>
{% endif %}
{% if next_url %}
<a href="{{ next_url }}">Older posts</a>
{% endif %}
...