我们在浏览某个博客网站时,如果博客文章过多,往往都进行了分页显示,比如CSDN中个人博客的首页显示。在flask中SQLAlchemy不但可以进行数据建模和数据查询等等,由于其支持分页查询,我们还可以利用其对我们的文章进行分页。
一、创建博客数据模型
在介绍使用SQLAlchemy对博客进行分页之前,我们需要先创建博客的数据模型,并向数据库中插入试验用的博客的数据。我们将博客的数据模型定义如下:
-
class Post(db.Model):
-
__tablename__='posts'
-
id=db.Column(db.Integer,primary_key=True)
-
body=db.Column(db.Text)
-
timestamp=db.Column(db.DateTime,index=True,default=datetime.utcnow)
博客的数据表中只含有3个字段:博客id、博客内容和时间戳,定义完成之后,我们向数据库中插入两个Post对象,以此作为试验数据:
二、定义视图函数,利用SQLAlchemy进行分页查询
-
@main.route('/',methods=['GET','POST'])
-
def index():
-
page=request.args.get('page',1,type=int)
-
pagination=Post.query.paginate(page,per_page=1,error_out=False)
-
posts=pagination.items
-
return render_template('index.html',posts=posts,pagination=pagination)
通过request.args.get我们可以获取一个url所带的参数,比如 http://localhost:5000/?page=1 携带参数page,值为1。request.args.get('page',1,type=int)的意思是从request的参数中获取参数page的值,如果参数不存在那么返回默认值1,type=int保证返回的默认值是整形数字。
SQLAlchemy中查询对象query的paginate方法返回一个分页对象pagination,Post.query.pagination(page,per_page=1,error_out=False)中第一个参数表示我们要查询的页数,这里用了上面获取的url的参数;第二个参数是每页显示的数量,我们这里设置成了1,如果不设置默认显示20条;第3个参数如果设置成True,当请求的页数超过了总的页数范围,就会返回一个404错误,如果设为False,就会返回一个空列表。
通过上面的分页,如果我们简单在index.html进行如下设置,就可以实现根据不同参数的url显示不同的博客文章:
index.html:
-
{% for post in posts %}
-
{{ post.body }}
-
{% endfor %}
url:http://localhost:5000/?page=1
url:http://localhost:5000/?page=2
三、添加分页导航
上面只是简单根据不同的url参数显示不同的博客文章,在实际应用时我们还需要给分页添加分页导航。paginate()方法返回的Pagination类对象包含很多属性和方法,我们可以利用它的属性和方法实现分页导航。它的属性和方法如下:
属性 | 说明 |
items | 当前页面的记录 |
query | 分页的源查询 |
page | 当前的页数 |
prev_num | 上一页数 |
next_num | 下一页数 |
has_prev | 是否有上一页 |
has_next | 是否有下一页 |
pages | 总的页数 |
per_page | 每页显示的记录条数 |
total | 总的记录条数 |
方法 | 说明 |
iter_pages (left_edge=2, left_current=2,right_current=5,right_age=2) | 一个迭代器,返回选中当前页时显示的页数列表。如这个例子中最左边显示2个页数,当前页的左边显示2个页数,当前页的右边显示5个页数而当前页的最右边显示2个页数 |
prev() | 上一页的分页对象 |
next() | 下一页的分页对象 |
使用上面的属性和方法,我们可以修改index.html文件,对当前的博客列表实现分页导航:
-
<!DOCTYPE html>
-
<html>
-
<head>
-
<meta charset="utf-8" />
-
<style type="text/css">
-
#menu {
-
font:12px verdana, arial, sans-serif; /* 设置文字大小和字体样式 */
-
}
-
#menu, #menu li {
-
list-style:none; /* 将默认的列表符号去掉 */
-
padding:0; /* 将默认的内边距去掉 */
-
margin:0; /* 将默认的外边距去掉 */
-
}
-
</style>
-
<style type="text/css">
-
#menu li {
-
float:left; /* 往左浮动 */
-
}
-
</style>
-
<style type="text/css">
-
#menu li a {
-
display:block; /* 将链接设为块级元素 */
-
padding:8px 50px; /* 设置内边距 */
-
background:#3A4953; /* 设置背景色 */
-
color:#fff; /* 设置文字颜色 */
-
text-decoration:none; /* 去掉下划线 */
-
border-right:1px solid #000; /* 在左侧加上分隔线 */
-
}
-
</style>
-
<title></title>
-
</head>
-
<body>
-
{% for post in posts %}
-
{{ post.body }}
-
{% endfor %}
-
<ul id="menu">
-
<li>
-
<a href="{% if pagination.has_prev %}{{url_for('.index',page=pagination.page-1)}}{% else %}#{% endif %}" >«</a>
-
</li>
-
{% for p in pagination.iter_pages() %}
-
<li>
-
<a href="{{ url_for('.index',page=p)}}">{{p}}</a>
-
</li>
-
{% endfor %}
-
<li>
-
<a href="{%if pagination.has_next %}{{url_for('.index',page=pagination.page+1)}}{% else %}#{% endif %}" >»</a>
-
</li>
-
</ul>
-
</body>
-
</html>
我们重点关注li标签中针对pagination的操作,首先根据当前的pagination是否有上一页对a标签进行了不同href的赋值,创建出<<的超链接;与之对应的是在最后根据当前pagination是否有下一页而再次对a标签赋予不同的url,创建出>>超链接;中间调用pagination的迭代器iter_pages(),便利每个分页对象,在以每个分页对象定义不同的超链接。
实现的效果如下: