Flask学习笔记-(八)分页功能实现

分页实现

1. 发布用户帖子

1.1. 新建用户帖子表单类

​ 修改app/forms.py脚本,新增用户帖子表单类,用于帖子信息的提交。

class PostForm(FlaskForm):
    """帖子提交表单"""
    post = TextAreaField('内容', validators=[DataRequired(), Length(min=1, max=140)])
    submit = SubmitField('提交')

1.2. 将帖子表单添加到首页模板中

​ 修改app/templates/index/index.html文件,增加帖子提交表单。

{% extends 'base.html' %}

{% block content %}
    <h1>Hi, {{ current_user.username }}!</h1>
    <form action="" method="post">
        {{ form.hidden_tag() }}
        <p>
            {{ form.post.label }}<br>
            {{ form.post(cols=32, rows=4) }}<br>
            {% for error in form.post.errors %}
                <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        </p>
        <p>{{ form.submit() }}</p>
    </form>
    {% for post in posts %}
        {% include 'common/post.html' %}
    {% endfor %}
{% endblock %}

1.3. 视图函数处理表单数据

​ 修改app/index.py脚本,修改原视图函数处理,增加表单数据的提交及查询展示。其中通过followed_posts方法可以获取本人以及其关注者对应的帖子信息。

from flask.views import View
from flask import render_template, flash, redirect, url_for
from flask_login import login_required, current_user

from app import db
from app.forms import PostForm
from app.models import Post


class IndexView(View):
    methods = ['GET', 'POST']
    decorators = [login_required]

    def dispatch_request(self):
        form = PostForm()
        if form.validate_on_submit():
            post = Post(body=form.post.data, author=current_user)
            db.session.add(post)
            db.session.commit()
            flash('你的帖子已提交!')
            return redirect(url_for('index'))

        # 获取本人及关注者对应的帖子信息
        posts = current_user.followed_posts().all()
        return render_template('index/index.html', title='首页', form=form, posts=posts)

1.4. 启动服务测试

​ 通过john用户登录首页。

Flask学习笔记-(八)分页功能实现

​ 发表一篇帖子信息。

Flask学习笔记-(八)分页功能实现

2. 新增发现页面展示所有用户的帖子

2.1. 新增发现视图函数

​ 修改app/index.py脚本,增加发现视图函数。

class ExploreView(View):
    """发现视图"""
    methods = ['GET']
    decorators = [login_required]

    def dispatch_request(self):
        posts = Post.query.order_by(Post.timestamp.desc()).all()
        return render_template('index/index.html', title='发现', posts=posts)

2.2. 注册发现视图

​ 修改app/__init__.py脚本,增加发现视图注册。

def create_app(test_config=None):
......
    # 注册Index首页视图URL
    from app.index import IndexView, ExploreView
    application.add_url_rule('/', view_func=IndexView.as_view('index'))
    # 注册Explore发现视图URL
    application.add_url_rule('/explore', view_func=ExploreView.as_view('explore'))
......

2.3.首页增加是否传入表单判断

​ 修改app/templates/index/index.html文件,增加表单是否传入判断,若未传入表单,则不展示帖子表单提交处理。

{% extends 'base.html' %}

{% block content %}
    <h1>Hi, {{ current_user.username }}!</h1>
    {% if form %}
        <form action="" method="post">
......
        </form>
    {% endif %}
......

2.4. base页面增加发现页面链接

​ 修改app/templates/base.html文件,增减发现页面链接。

......
<body>
    <div>
        博客:
        <a href="{{ url_for('index') }}">首页</a>
        <a href="{{ url_for('explore') }}">发现</a>
......

2.3. 将帖子子模板中作者信息修改为链接

​ 修改app/templates/common/post.html文件,将作者用户名修改为链接方式。

    <table>
        <tr valign="top">
            <td><img src="{{ get_avatars(post.author.email, 36) }}"></td>
            <td>
                <a href="{{ url_for('user.user_info', username=post.author.username) }}">
                    {{ post.author.username }}
                </a> 
                says:<br>{{ post.body }}
            </td>
        </tr>
    </table>

2.4. 启动服务测试

​ 通过用户john登录。

Flask学习笔记-(八)分页功能实现

​ 通过用户susan登录,并新增帖子。

Flask学习笔记-(八)分页功能实现

Flask学习笔记-(八)分页功能实现

​ 点击发现链接。

Flask学习笔记-(八)分页功能实现

​ 点击用户john的用户名链接。

Flask学习笔记-(八)分页功能实现

3. 用户帖子分页处理

3.1. 给主页及发现页视图函数增加分页处理

​ 修改app/index.py脚本,增加分页处理。其中,查询页数通过参数page以查询字符串参数方式指定,该参数可以通过request.args对象获取,默认为第一页。

分页处理函数paginate

  • 第一个参数为当前查询页码数
  • 第二个参数为每页查询数据条数(设置到config.py文件中,测试先设置为2条)
  • 第三个参数为错误处理布尔标记,如果是True,当请求范围超出已知范围时自动引发404错误;如果是False,则会返回一个空列表
class IndexView(View):
    methods = ['GET', 'POST']
    decorators = [login_required]

    def dispatch_request(self):
......
        # 获取本人及关注者对应的帖子信息
        page = request.args.get('page', 1, type=int)
        # 分页处理
        posts = current_user.followed_posts().paginate(page, current_app.config['POSTS_PER_PAGE'], False)
        return render_template('index/index.html', title='首页', form=form, posts=posts)
    
    
class ExploreView(View):
    """发现视图"""
    methods = ['GET']
    decorators = [login_required]

    def dispatch_request(self):
        page = request.args.get('page', 1, type=int)
        # 分页处理
        posts = Post.query.order_by(Post.timestamp.desc()).paginate(page, current_app.config['POSTS_PER_PAGE'], False)
        return render_template('index/index.html', title='发现', posts=posts)

3.2. 新增分页导航视图

paginate方法返回一个Pagination的实例,该实例有几个属性可以用来构建分页链接:

  • items:请求内容的数据列表

  • has_next: 当前页之后存在后续页面时为真

  • has_prev: 当前页之前存在前置页面时为真

  • next_num: 下一页的页码

  • prev_num: 上一页的页码

    通过这几个元素,修改app/index.py脚本,可以生成上一页和下一页的链接并将其传入模板以渲染。url_for()函数有一个特点,可以添加任何关键字参数,如果这些参数的名字没有直接在URL中匹配使用,那么Flask将它们设置为URL的查询字符串参数。

class IndexView(View):
    methods = ['GET', 'POST']
    decorators = [login_required]

    def dispatch_request(self):
......
        # 获取本人及关注者对应的帖子信息
        page = request.args.get('page', 1, type=int)
        # 分页处理
        posts = current_user.followed_posts().paginate(page, current_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/index.html', title='首页', form=form, posts=posts.items,
                               next_url=next_url, prev_url=prev_url, page=page)
    
    
class ExploreView(View):
    """发现视图"""
    methods = ['GET']
    decorators = [login_required]

    def dispatch_request(self):
        page = request.args.get('page', 1, type=int)
        # 分页处理
        posts = Post.query.order_by(Post.timestamp.desc()).paginate(page, current_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/index.html', title='发现', posts=posts.items,
                               next_url=next_url, prev_url=prev_url, page=page)

3.3. 新增分页导航模板

​ 修改app/templates/index/index.html文件,增加分页导航展示。

......
    {% for post in posts %}
        {% include 'common/post.html' %}
    {% endfor %}
    <p>
        {% if prev_url %}
            <a href="{{ prev_url }}">上一页</a>
        {% endif %}
        &nbsp;第{{ page }}页&nbsp;
        {% if next_url %}
            <a href="{{ next_url }}">下一页</a>
        {% endif %}
    </p>
{% endblock %}

3.4. 启动服务测试

​ 通过用户susan登录,该用户有两条帖子,只展示当前页数。

Flask学习笔记-(八)分页功能实现

​ 点击发现链接,展示所有用户的帖子信息,总共有三条,则会展示下一页链接。

Flask学习笔记-(八)分页功能实现

​ 点击下一页链接,展示另外一条帖子信息,并显示上一页链接。

Flask学习笔记-(八)分页功能实现

Flask学习笔记-(八)分页功能实现

4. 个人主页分页处理

4.1. 视图函数增加分页处理

​ 修改app/user.py脚本,增加分页处理。

class UserInfoView(View):
    """用户信息查询"""
    methods = ['GET']
    decorators = [login_required]

    def dispatch_request(self, username):
        user = User.query.filter_by(username=username).first_or_404()
        page = request.args.get('page', 1, type=int)
        posts = user.posts.order_by(Post.timestamp.desc()).paginate(page, current_app.config['POSTS_PER_PAGE'], False)
        next_url = url_for('user.user_info', username=user.username, page=posts.next_num) if posts.has_next else None
        prev_url = url_for('user.user_info', username=user.username, page=posts.prev_num) if posts.has_prev else None
        return render_template('user/user_info.html', user=user, posts=posts.items,
                               next_url=next_url, prev_url=prev_url, page=page)

4.2. 模板添加分页导航

​ 修改app/templates/user/user_info.html文件,增减分页导航链接。

......
	<hr>
    {% for post in posts %}
        {% include 'common/post.html' %}
    {% endfor %}
    <p>
        {% if prev_url %}
            <a href="{{ prev_url }}">上一页</a>
        {% endif %}
        &nbsp;第{{ page }}页&nbsp;
        {% if next_url %}
            <a href="{{ next_url }}">下一页</a>
        {% endif %}
    </p>
{% endblock %}

4.3. 启动服务测试

​ 通过用户susan登录并新增一条帖子。

Flask学习笔记-(八)分页功能实现

​ 点击个人资料链接,也会有分页处理。

Flask学习笔记-(八)分页功能实现

Flask学习笔记-(八)分页功能实现

5. 调整分页时每页数据条数参数

​ 修改app/config.py脚本,将条数调整为每页20条。

# -----------分页参数配置-------------#
# 每页展示数据条数
POSTS_PER_PAGE = 20