Flask学习笔记:分页

充煌
2023-12-01
1. 做好准备工作
  • 进入项目主目录
  • 激活虚拟环境


2. 博客帖子分页

Flask-Alchemy的paginate()查询方法支持分页。比如说,我想要获取用户第一组20个的关注帖子,我可以将语句最后的all()替换为:

>>> user.followed_posts().paginate(1, 20, False).items

paginate方法可以被Flask-SQLAlchemy的任何查询对象调用。它接收三个参数:

  • 页码,从1开始
  • 每页的条目数
  • 错误标志。如果设为True,当请求超过范围的页面时,它会自动返回一个404错误到客户端。如果设为False,请求超范围的页面会返回一个空列表。

paginate的返回值是一个Pagination对象。该对象的items属性包含请求页面的条目列表。

现在来实现index()视图函数的分页功能。首先,向应用添加一个配置项,用于确定每页显示多少个条目。

config.py

class Config(object):
    # ...
    POSTS_PER_PAGE = 3

将页码并入应用URL的常用方法是使用查询字符串参数来指定可选的页码,如果该参数没有给出则默认为1。下面是一些示例URL:

  • 第一页,隐式:http://localhost:5000/index
  • 第一页,显式:http://localhost:5000/index?page=1
  • 第三页:http://localhost:5000/index?page=3

要获取查询字符串的参数,可以使用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对象确定了页面的条目数量。


3. 页面导航

下一个更改是在博客帖子列表的底部添加链接,使用户可以切换到下一页/上一页。前面提到,paginate()的返回值是Flask-SQLAlchemy的Pagination类的对象,除了上面用到的items属性之外,该对象还有其他几个对于创建分页对象有用的属性:

  • has_next:如果当前页面之后还有至少一个页面时为True
  • has_prev:如果当前页面之前还有至少一个页面时为True
  • next_num:下一页的页码
  • prev_num:上一页的页码

有了这四个元素,我就可以生成下一页和上一页的链接并传递给模板来渲染了:

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 %}
    ...

 类似资料: