1. 程式人生 > >Flask 系列之 Pagination

Flask 系列之 Pagination

boot 通過 設置 分享圖片 aps port 技術分享 secondary ted

技術分享圖片

說明

  • 操作系統:Windows 10
  • Python 版本:3.7x
  • 虛擬環境管理器:virtualenv
  • 代碼編輯器:VS Code

實驗目標

實現當前登錄用戶的事務瀏覽、添加、刪除 操作

實現

首先,在我們的 todolist\forms.py 中添加事務添加對應的表單類 ThingForm,示例代碼如下所示:

from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, TextAreaField, PasswordField
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
from models import User


class RegisterForm(FlaskForm):
    username = StringField('用戶名:', validators=[
                           DataRequired(), Length(min=6, max=20)])
    email = StringField('郵箱:', validators=[DataRequired(), Email()])
    pwd = PasswordField('密碼:', validators=[
        DataRequired(), Length(min=8, max=120)])
    confirm = PasswordField('確認密碼:', validators=[
                            DataRequired(), EqualTo('pwd')])
    submit = SubmitField('提交')

    def validate_username(self, username):
        user = User.query.filter_by(name=username.data).first()
        if user:
            raise ValidationError("用戶昵稱已存在。")

    def validate_email(self, email):
        user = User.query.filter_by(email=email.data).first()
        if user:
            raise ValidationError('郵箱已存在.')


class LoginForm(FlaskForm):
    username = StringField('用戶名:', validators=[
                           DataRequired(), Length(min=6, max=20)])
    password = PasswordField('密碼:', validators=[DataRequired()])
    submit = SubmitField('登陸')

    def validate_username(self, username):
        user = User.query.filter_by(name=username.data)
        if not user:
            raise ValidationError('用戶名不存在。')


class ThingForm(FlaskForm):
    title = StringField('標題:', validators=[
                        DataRequired(), Length(min=6, max=20)])
    text = TextAreaField('內容:', validators=[DataRequired()])
    submit = SubmitField('提交')

接著修改 todolist\app\views.py ,添加當前用戶事務的添加、刪除,示例代碼如下所示:

from flask import render_template, redirect, url_for, flash, request
from flask_login import login_user, login_required, current_user, logout_user
from app import app, db
from forms import ThingForm, RegisterForm, LoginForm
from models import User, Thing


@app.context_processor
def inject_user():
    user = User.query.first()
    return dict(user=user)


@app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
def index():
    form = ThingForm()
    if not current_user.is_authenticated:
        return redirect(url_for('login'))
    if request.method == 'POST' and form.validate_on_submit():
        user_id = current_user.id
        title = form.title.data
        text = form.text.data
        thing = Thing(user_id=user_id, title=title, text=text)
        db.session.add(thing)
        db.session.commit()
        flash('添加成功')
    page = request.args.get('page', 1, type=int)
    things = current_user.things.order_by(
        Thing.add_date.desc()).paginate(page, 2, False)
    print(things)
    return render_template('index.html', title="首頁", form=form, things=things)


@app.route('/login', methods=['POST', 'GET'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        name = form.username.data
        pwd = form.password.data
        user = User.query.filter_by(name=name).first()
        if user and user.check_password_hash(pwd):
            login_user(user)
            flash('登陸成功。', category='info')
            return redirect(url_for('index'))
        else:
            flash("密碼或賬戶錯誤。", category='error')
    return render_template('login.html', title='登錄', form=form)


@app.route('/logout')
@login_required
def logout():
    logout_user()
    flash('再見!')
    return redirect(url_for('login'))


@app.route('/register', methods=['POST', 'GET'])
def register():
    form = RegisterForm()
    if form.validate_on_submit():
        username = form.username.data
        email = form.email.data
        pwd = form.pwd.data
        user = User(name=username, email=email)
        user.generate_password_hash(pwd)
        db.session.add(user)
        db.session.commit()
        flash('註冊成功', category='info')
        return redirect(url_for('login'))
    return render_template('register.html', title='註冊', form=form)


@app.route('/delete/<int:id>')
@login_required
def delete(id):
    thing = Thing.query.get(id)
    if thing:
        db.session.delete(thing)
        db.session.commit()
        return redirect(url_for('index'))

最後,完善 todolist\app\templates\index.html,添加數據展示相關代碼,示例代碼如下所示:

{% extends 'base.html' %} {% block content %} {% if current_user.is_authenticated and user %}
<h1 class="m-4">{{ current_user.name }},歡迎回來</h1>
{% endif %}

<div class="container-fluid">
    <p>
        <a class="btn btn-primary" data-toggle="collapse" href="#collapseExample" role="button" aria-expanded="false" aria-controls="collapseExample">
            添加新事務
        </a>
    </p>
    <div class="collapse" id="collapseExample">
        <div class="card card-body mb-4">
            {% from 'bootstrap/form.html' import render_form %} {{ render_form(form) }}
        </div>
    </div>

    <ul class="list-group">
        {% for thing in things.items %}
        <li class="list-group-item">
            <h4 style="display:block;float:left;padding-top:2px">
                {{ thing.title }}
            </h4>
            <div style="display:block;float: right;">
                <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModalCenter{{thing.id}}">查看</button>
                <a class="btn btn-danger" href='/delete/{{ thing.id }}'>刪除</a>
            </div>
        </li>

        <div class="modal fade" id="exampleModalCenter{{thing.id}}" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
            <div class="modal-dialog modal-dialog-centered" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title" id="exampleModalLongTitle">{{ thing.title }}</h5>
                    </div>
                    <div class="modal-body">
                        {{ thing.text }}
                    </div>
                    <div class="modal-footer">
                        <small>{{ thing.add_date }}</small>
                        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                    </div>
                </div>
            </div>
        </div>
        {% endfor %}
    </ul>

    <nav aria-label="Page navigation example" class="m-4">
        <ul class="pagination justify-content-center">
            <li class="page-item {% if not things.has_prev %}disabled{% endif %}">
                <a class="page-link" href="{{ url_for('index',page=things.prev_num) }}">上一頁</a>
            </li>

            {% for page in things.iter_pages(1,1,3,2) %} {% if page %}
            <li class="page-item {%if page==things.page%}active{%endif%}">
                <a class="page-link" href="{{ url_for('index',page=page) }}">{{page}}</a>
            </li>
            {% else %}
            <li class="page-item disabled">
                <a class="page-link" href="#">&hellip;</a>
            </li>
            {% endif %} {% endfor %}

            <li class="page-item {% if not things.has_next %}disabled{% endif %}">
                <a class="page-link" href="{{ url_for('index',page=things.next_num) }}">下一頁</a>
            </li>
        </ul>
    </nav>

</div>

{% endblock %}

此時,當我們運行起我們的網站後進入註冊頁面 http://127.0.0.1:5000 就可以進行當前登錄用戶的事務錄入、查看、刪除、和事務分頁的效果了。

補充

一個 Pagination 對象的常用屬性有:

  • items 當前頁面中的所有記錄(比如當前頁上有5條記錄,items就是以列表形式組織這5個記錄)
  • query 當前頁的query對象(通過query對象調用paginate方法獲得的Pagination對象)
  • page 當前頁碼(比如當前頁是第5頁,返回5)
  • prev_num 上一頁頁碼
  • next_num 下一頁頁碼
  • has_next 是否有下一頁 True/False
  • has_prev 是否有上一頁 True/False
  • pages 查詢得到的總頁數 per_page 每頁顯示的記錄條數
  • total 總的記錄條數

常用方法有:

  • prev() 上一頁的分頁對象Pagination
  • next() 下一頁的分頁對象Pagination
  • iter_pages(left_edge=2,left_current=2,right_current=5,right_edge=2)
  • iter_pages 用來獲得針對當前頁的應顯示的分頁頁碼列表。
  • 假設當前共有100頁,當前頁為50頁,按照默認的參數設置調用iter_pages獲得的列表為:[1,2,None,48,49,50,51,52,53,54,55,None,99,100]

Flask 系列之 Pagination