# 26.分页

## 论述

flask通过flask-sqlalchemy建立模型后，将数据库的数据查询出来传递到模板中，由于有时候数据很多，需要进行分页查询，自己动手写一个分页很麻烦。可以通过flask-sqlalchemy自带的分页功能或者flask-pagination、flask-pagninate等插件进行分页。

## 创建数据库

```
create database pagination charset=utf8;
```

![image-20200915130950146](https://cdn.jsdelivr.net/gh/TheFoxFairy/notebook-picgo@master/img/20200926192146.png)

## 配置数据库

```
from flask import Flask,render_template,request
from flask_sqlalchemy import SQLAlchemy

HOSTNAME = "127.0.0.1"
PORT = "3306"
DATABASE = "pagination"
USERNAME = "root"
PASSWORD = "123456"
DB_URI = "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = DB_URI
# 屏蔽SQLalchemy发送的信号
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

db = SQLAlchemy()
db.init_app(app)
```

## 定义模型

```
class User(db.Model):
    __tablename__ = "user"
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    name = db.Column(db.String(200),)
    age = db.Column(db.Integer,)
```

## 创建数据

通过python shell进行交互创建数据

```
from app import *
db.create_all()
```

如果出现如下错误

```
'No application found. Either work inside a view function or push'。。。
```

则应该这样写

```
db.create_all(app=app)
```

接下来进行创建数据

```
from app import *

with app.app_context():
    for i in range(1000):
        user = UserModel(name="user{}".format(i),age=i%100)
        db.session.add(user)
    db.session.commit()
```

查看数据从第10行开始的10条数据

![image-20200915131548538](https://cdn.jsdelivr.net/gh/TheFoxFairy/notebook-picgo@master/img/20200926192147.png)

## 分页操作

### flask-sqlalchemy的分页功能

通过flask-sqlalchemy自带的分页功能进行分页

#### 定义视图函数

```
@app.route('/')
def index():
    # 获取页码，page是分页操作中默认的参数
    page = request.args.get("page",default=1,type=int)
    # 获取sqlalchemy对象
    objs = UserModel.query.order_by(UserModel.name.desc())
    # 获取pagination对象
    pagination = objs.paginate(page, per_page=10, error_out = False)
    # 通过pagination对象的items方法返回当前页的内容列表
    users = pagination.items

	# pagination对象能够进行分页
    data = {
        "users":users,
        "pagination":pagination,
    }
    
    return render_template("index.html",**data)
```

#### pagination对象常用方法

| 参数                                                                          | 用法                                           |
| --------------------------------------------------------------------------- | -------------------------------------------- |
| has\_next                                                                   | 是否还有下一页                                      |
| has\_prev                                                                   | 是否还有上一页                                      |
| **items**                                                                   | 返回当前页的所有内容                                   |
| next(error\_out=False)                                                      | 返回下一页的Pagination对象                           |
| prev(error\_out=False)                                                      | 返回上一页的Pagination对象                           |
| **page**                                                                    | 当前页的页码(从1开始)                                 |
| pages                                                                       | 总页数                                          |
| **per\_page**                                                               | 每页显示的数量                                      |
| prev\_num                                                                   | 上一页页码数                                       |
| next\_num                                                                   | 下一页页码数                                       |
| query                                                                       | 返回创建的Pagination对象的查询对象                       |
| total                                                                       | 查询返回的记录总数                                    |
| iter\_pages(left\_edge=2, left\_current=2, right\_current=5, right\_edge=2) | 迭代分页中的页码，四个参数，分别控制了省略号左右两侧各显示多少页码，在模板中可以这样渲染 |

#### 定义模板

**index.html**

```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div data-gb-custom-block data-tag="for">

{{ item.name }} -- {{ item.age }} <br>

</div>

<div data-gb-custom-block data-tag="set" data-endpoint='index'></div>

<ul class="pagination">
{#    没有前一页，添加class=disble，不可点击，a href=##}
    <li

<div data-gb-custom-block data-tag="if"> class="disabled"</div>>
        <a href="<div data-gb-custom-block data-tag="if"></div>{{ url_for(endpoint, page=pagination.prev_num)}}<div data-gb-custom-block data-tag="else">#</div>">
            &laquo;
        </a>
    </li>
    <div data-gb-custom-block data-tag="for"></div>

        

<div data-gb-custom-block data-tag="if"></div>

            

<div data-gb-custom-block data-tag="if">

            <li class="active">
                <a href="{{ url_for(endpoint, page = p)}}">{{ p }}</a>
            </li>
            

<div data-gb-custom-block data-tag="else">

            <li>
                <a href="{{ url_for(endpoint, page = p) }}">{{ p }}</a>
            </li>
            

</div>

        

<div data-gb-custom-block data-tag="else">

        <li class="disabled"><a href="#">&hellip;</a></li>
        

</div>

    

</div>

{#    没有后一页，添加class=disble，不可点击，a href=##}
    <li

<div data-gb-custom-block data-tag="if"> class="disabled"</div>>
        <a href="<div data-gb-custom-block data-tag="if"></div>{{ url_for(endpoint, page=pagination.next_num) }}<div data-gb-custom-block data-tag="else">#</div>

">
            &raquo;
        </a>
    </li>
</ul>
</body>
</html>
```

![image-20200915134650429](https://cdn.jsdelivr.net/gh/TheFoxFairy/notebook-picgo@master/img/20200926192148.png)

可以将分页的部分抽离出来放在macro模板中，这样其他页面需要分页的时候，就能直接调用时候了，不用在写一次了。

**\_macro.html**

```
<div data-gb-custom-block data-tag="macro"></div>

<ul class="pagination">
{#    没有前一页，添加class=disble，不可点击，a href=##}
    <li

<div data-gb-custom-block data-tag="if"> class="disabled"</div>>
        <a href="<div data-gb-custom-block data-tag="if"></div>{{ url_for(endpoint, page=pagination.prev_num)}}<div data-gb-custom-block data-tag="else">#</div>">
            &laquo;
        </a>
    </li>
    <div data-gb-custom-block data-tag="for"></div>

        

<div data-gb-custom-block data-tag="if"></div>

            

<div data-gb-custom-block data-tag="if">

            <li class="active">
                <a href="{{ url_for(endpoint, page = p)}}">{{ p }}</a>
            </li>
            

<div data-gb-custom-block data-tag="else">

            <li>
                <a href="{{ url_for(endpoint, page = p) }}">{{ p }}</a>
            </li>
            

</div>

        

<div data-gb-custom-block data-tag="else">

        <li class="disabled"><a href="#">&hellip;</a></li>
        

</div>

    

</div>

{#    没有后一页，添加class=disble，不可点击，a href=##}
    <li

<div data-gb-custom-block data-tag="if"> class="disabled"</div>>
        <a href="<div data-gb-custom-block data-tag="if">{{ url_for(endpoint, page=pagination.next_num) }}<div data-gb-custom-block data-tag="else">#</div>

">
            &raquo;
        </a>
    </li>
</ul>

</div>
```

**index.html**

```
<div data-gb-custom-block data-tag="import" data-0='_macro.html'></div>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div data-gb-custom-block data-tag="for">

{{ item.name }} -- {{ item.age }} <br>

</div>

{{ macros.render_pagination(pagination=pagination,endpoint="index") }}

</body>
</html>
```

![image-20200915134821028](https://cdn.jsdelivr.net/gh/TheFoxFairy/notebook-picgo@master/img/20200926192149.png)

### 通过flask-pagination进行分页

#### 安装

```
pip install flask-paginate
```

#### 导入模块

```
from flask_paginate import Pagination,get_page_parameter
```

* `get_page_parameter()` 这个默认值为 `page`, 也就是分页编号, 表示当前是第几页

#### 定义视图函数

```
@app.route('/')
def index(per_page=10):
    # 获取页码，page是分页操作中默认的参数
    page = request.args.get(get_page_parameter(),type=int,default=1)

    offset = (page-1)*per_page
    count = offset + per_page

    # 获取sqlalchemy对象
    objs = UserModel.query
    """
    objs = UserModel.query.all()[offset:count]
    """

    # 用户数据
    users = objs.offset(offset).limit(per_page).all()

    # 获取总数
    total = objs.count()
    # 获取pagination对象
    pagination = Pagination(bs_version=3, page=page, total=total, outer_window=0, inner_window=2)

    data = {
        "users":users,
        "pagination":pagination,
    }
    return render_template("index.html",**data)
```

`Pagination(bs_version=3, page=page, total=total, outer_window=0, inner_window=2)`中需要添加`outer_window=0, inner_window=2`这样能够渲染按钮。

#### pagination对象常用方法

| 参数                                                                                        | 说明                                                                                                                                                                                                         |
| ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Pagination(page=,total=,bs\_version=,search=,record\_name=,outer\_window=,inner\_window=) | `page=` 当前是第几页; `total=` 数据总量; `bs_version=` 这个就是 `bootstrap` 的版本号了, 默认值是2 ;`search=` 是否是搜索, `pagination.info` 格式化时文案会不一样 ;`record_name=` 展示文案 `pagination.info` 中的值;设置`outer_window`,`inner_window`能够渲染按钮 |
| links                                                                                     | 一组可点击的分页页码的展示                                                                                                                                                                                              |
| info                                                                                      | 分页数据总量的展示                                                                                                                                                                                                  |

#### 更改CSS样式

可以根据需求调整

```
.pagination-page-info {
    padding: .6em;
    padding-left: 0;
    width: 40em;
    margin: .5em;
    margin-left: 0;
    font-size: 12px;
}
.pagination-page-info b {
    color: black;
    background: #6aa6ed;
    padding-left: 2px;
    padding: .1em .25em;
    font-size: 150%;
}
```

#### 定义模板

```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
</head>
<body>

<div data-gb-custom-block data-tag="for">

{{ user.name }} -- {{ user.age }} <br>

</div>
{{ pagination.info }}
{{ pagination.links }}
</body>
</html>
```

![image-20200915143750693](https://cdn.jsdelivr.net/gh/TheFoxFairy/notebook-picgo@master/img/20200926192150.png)
