Flask学习之九 分页
英文博客地址:http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-ix-pagination
中文翻译地址:http://www.pythondoc.com/flask-mega-tutorial/pagination.html
开源中国社区:http://www.oschina.net/translate/the-flask-mega-tutorial-part-ix-pagination
一、Blog Posts的提交
之前我们的程序用的都是定义的blog,现在我们需要有一个地方让用户发表blog。
首先我们定义一个单字段的表单对象(文件 app/forms.py):
class PostForm(Form):
post = StringField('post', validators=[DataRequired()])
接着,把表单添加到模板中(文件 app/templates/index.html):
<!-- extend base layout -->
{% extends "base.html" %} {% block content %}
<h1>Hi, {{ g.user.nickname }}!</h1>
<form action="" method="post" name="post">
{{ form.hidden_tag() }}
<table>
<tr>
<td>Say something:</td>
<td>{{ form.post(size=30, maxlength=140) }}</td>
<td>
{% for error in form.post.errors %}
<span style="color: red;">[{{ error }}]</span><br>
{% endfor %}
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Post!"></td>
<td></td>
</tr>
</table>
</form>
{% for post in posts %}
<p>
{{ post.author.nickname }} says: <b>{{ post.body }}</b>
</p>
{% endfor %}
{% endblock %}
最后,把这一切联系起来的视图函数需要被扩展用来处理表单(文件 app/views.py):
from forms import LoginForm, EditForm, PostForm
from models import User, Post @app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
@login_required
def index():
form = PostForm()
if form.validate_on_submit():
post = Post(body=form.post.data, timestamp=datetime.utcnow(), author=g.user)
db.session.add(post)
db.session.commit()
flash('Your post is now live!')
return redirect(url_for('index'))
posts = [
{
'author': {'nickname': 'John'},
'body': 'Beautiful day in Portland!'
},
{
'author': {'nickname': 'Susan'},
'body': 'The Avengers movie was so cool!'
}
]
return render_template('index.html',
title='Home',
form=form,
posts=posts)
不要忘了在与 index 视图函数相关联的两个路由上,接受 POST 请求,因为我们需要接受提交的 blog。
当我们在数据库中插入一个新的 Post 后,我们将会重定向到首页:
return redirect(url_for('index'))
为什么要重定向?想想当一个用户写下一个blog post请求之后,点击提交,然后再点击浏览器刷新按钮。刷新的命令将会做些什么?浏览器将会重新发送上一次的请求作为刷新命令的结果。
如果没有重定向,上一次的请求是提交表单的 POST 请求,因此刷新动作将会重新提交表单,导致与第一个相同的第二个 Post 记录被写入数据库。这并不好。
有了重定向,我们迫使浏览器在表单提交后发送另外一个请求,即重定向页的请求。这是一个简单的 GET 请求,因此一个刷新动作将会重复 GET 请求而不是多次提交表单。
这个小技巧避免了用户在提交 blog 后不小心触发刷新的动作而导致插入重复的 blog。
二、显示blog
从上面我们可以看到显示出来的blog内容依然不是数据库里的真实数据,我们需要把posts进行如下修改(文件 app/views.py):
posts = g.user.followed_posts().all()
如同上一章提到的一样,它允许我们获取关注的用户的所有的 blog。
现在可以试一下创建一些用户,让他们follow其他人,然后发布一些信息来看一下效果。
三、分页
我们把所有关注者的 blog 展示在首页上。如果数据量太大,处理如此大数据量的列表对象将会极其低效的。
我们可以显示把这么大量的post分组来显示,或者分页。
Flask-SQLAlchemy可以很好的支持分页。例如,我们可以通过如下方法,轻松获取某个用户的前3篇的followed posts:
posts = g.user.followed_posts().paginate(1, 3, False).items
paginate 方法能够被任何查询调用。它接受三个参数:
- 页数,从 1 开始,
- 每一页的项目数,这里也就是说每一页显示的 blog 数,
- 错误标志。如果是 True,当请求的范围页超出范围的话,一个 404 错误将会自动地返回到客户端的网页浏览器。如果是 False,返回一个空列表而不是错误。
从 paginate 返回的值是一个 Pagination 对象。这个对象的 items 成员包含了请求页面项目(我们这个程序中指 blog)的列表。
要实现分页,首先,在配置文件中添加一些决定每页显示的 blog 数的配置项(文件 config.py):
# pagination
POSTS_PER_PAGE = 3
之后,先知道URLs是如何判断请求不同的页面的。Flask的routes可以接受参数的,所以可以在URL添加后缀,来指明我们想要的页面:
http://localhost:5000/ <-- page #1 (default)
http://localhost:5000/index <-- page #1 (default)
http://localhost:5000/index/1 <-- page #1
http://localhost:5000/index/2 <-- page #2
这种格式的 URLs 能够轻易地通过在我们的视图函数中附加一个 route 来实现(文件 app/views.py):
from config import POSTS_PER_PAGE @app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
@app.route('/index/<int:page>', methods=['GET', 'POST'])
@login_required
def index(page=1):
form = PostForm()
if form.validate_on_submit():
post = Post(body=form.post.data, timestamp=datetime.utcnow(), author=g.user)
db.session.add(post)
db.session.commit()
flash('Your post is now live!')
return redirect(url_for('index'))
posts = g.user.followed_posts().paginate(page, POSTS_PER_PAGE, False).items
return render_template('index.html',
title='Home',
form=form,
posts=posts)
我们的新route接受分页参数,并把参数定义为整数。
因为那三个route当中有两个route没有分页参数,我们还需要添加分页参数到index函数当中,并给它们一个默认值。
四、页面导航
我们现在需要添加链接允许用户访问下一页以及/或者前一页,用户不可能自行更改url的。
在我们目前的视图函数中,我们使用的是:
posts = g.user.followed_posts().paginate(page, POSTS_PER_PAGE, False).items
我们只保留了paginate方法返回的Pagination对象的item成员。但是这个对象还有很多其它有用的东西在里面,因此我们还是使用整个对象(文件 app/views.py):
posts = g.user.followed_posts().paginate(page, POSTS_PER_PAGE, False)
这里修改后,还必须修改模板(文件 app/templates/index.html):
<!-- posts is a Paginate object -->
{% for post in posts.items %}
<p>
{{ post.author.nickname }} says: <b>{{ post.body }}</b>
</p>
{% endfor %}
这样模版能够使用完全的 Paginate 对象。我们使用的这个对象的成员有:
- has_next:如果在目前页后至少还有一页的话,返回 True
- has_prev:如果在目前页之前至少还有一页的话,返回 True
- next_num:下一页的页面数
- prev_num:前一页的页面数
这样要进行导航就很容易了,(文件 app/templates/index.html):
<!-- posts is a Paginate object -->
{% for post in posts.items %}
<p>
{{ post.author.nickname }} says: <b>{{ post.body }}</b>
</p>
{% endfor %}
{% if posts.has_prev %}<a href="{{ url_for('index', page=posts.prev_num) }}"><< Newer posts</a>{% else %}<< Newer posts{% endif %} |
{% if posts.has_next %}<a href="{{ url_for('index', page=posts.next_num) }}">Older posts >></a>{% else %}Older posts >>{% endif %}
< 是转义字符,表示“<”
五、实现 Post 子模板
之前我们定义了一个子模板,现在是时候在我们的首页上也包含这个子模板。(文件 app/templates/index.html):
<!-- posts is a Paginate object -->
{% for post in posts.items %}
{% include 'post.html' %}
{% endfor %}
六、用户信息页
我们在用户信息页上显示了 blog。为了保持一致性,用户信息页也跟首页一样。
改变是跟修改首页一样的。这是我们需要做的列表:
- 添加一个额外的路由获取页面数的参数
- 添加一个默认值为 1 的 page 参数到视图函数
- 用合适的数据库查询与分页代替伪造的 blog
- 更新模板使用分页对象
下面就是更新后的视图函数(文件 app/views.py):
@app.route('/user/<nickname>')
@app.route('/user/<nickname>/<int:page>')
@login_required
def user(nickname, page=1):
user = User.query.filter_by(nickname=nickname).first()
if user is None:
flash('User %s not found.' % nickname)
return redirect(url_for('index'))
posts = user.posts.paginate(page, POSTS_PER_PAGE, False)
return render_template('user.html',
user=user,
posts=posts)
注意上面的视图函数已经有一个 nickname 参数,我们把 page 作为它的第二个参数。
模版的改变同样很简单(文件 app/templates/user.html):
<!-- posts is a Paginate object -->
{% for post in posts.items %}
{% include 'post.html' %}
{% endfor %}
{% if posts.has_prev %}<a href="{{ url_for('user', nickname=user.nickname, page=posts.prev_num) }}"><< Newer posts</a>{% else %}<< Newer posts{% endif %} |
{% if posts.has_next %}<a href="{{ url_for('user', nickname=user.nickname, page=posts.next_num) }}">Older posts >></a>{% else %}Older posts >>{% endif %}
Flask学习之九 分页的更多相关文章
- Flask学习之旅--分页功能:分别使用 flask--pagination 和分页插件 layPage
一.前言 现在开发一个网站,分页是一个很常见的功能了,尤其是当数据达到一定量的时候,如果都显示在页面上,会造成页面过长而影响用户体验,除此之外,还可能出现加载过慢等问题.因此,分页就很有必要了. 分页 ...
- [ZHUAN]Flask学习记录之Flask-SQLAlchemy
From: http://www.cnblogs.com/agmcs/p/4445583.html 各种查询方式:http://www.360doc.com/content/12/0608/11/93 ...
- Bootstrap <基础十九>分页
Bootstrap 支持的分页特性.分页(Pagination),是一种无序列表,Bootstrap 像处理其他界面元素一样处理分页. 分页(Pagination) 下表列出了 Bootstrap 提 ...
- Flask 学习目录
Flask 学习目录 Flask 的学习过程是一个了解如何从单个模块搭建一个 Web 框架的过程. Python.Flask 的安装和设置 简单的 Hello World 程序 使用 Jinjia2 ...
- Python Flask学习笔记之模板
Python Flask学习笔记之模板 Jinja2模板引擎 默认情况下,Flask在程序文件夹中的templates子文件夹中寻找模板.Flask提供的render_template函数把Jinja ...
- Python Flask学习笔记之Hello World
Python Flask学习笔记之Hello World 安装virtualenv,配置Flask开发环境 virtualenv 虚拟环境是Python解释器的一个私有副本,在这个环境中可以安装私有包 ...
- 【转载】 强化学习(九)Deep Q-Learning进阶之Nature DQN
原文地址: https://www.cnblogs.com/pinard/p/9756075.html ------------------------------------------------ ...
- 多线程学习笔记九之ThreadLocal
目录 多线程学习笔记九之ThreadLocal 简介 类结构 源码分析 ThreadLocalMap set(T value) get() remove() 为什么ThreadLocalMap的键是W ...
- Flask学习-Wsgiref库
一.前言 前面在Flask学习-Flask基础之WSGI中提到了WerkZeug,我们知道,WerkZeug是一个支持WSGI协议的Server,其实还有很多其他支持WSGI协议的Server.htt ...
随机推荐
- myql 配置项
提高数据插入速度方法 bulk_insert_buffer_size 默认:8M (8*1024*1024) 参考网址:https://stackoverflow.com/questions/2030 ...
- JavaScript的坑,缺陷
JavaScript的缺陷 1.在做判断的时候用=======而不是== 2.浮点预算有精度问题 通过差值去把这个精度锁定到一个范围 Math. Abs(A-B)<0.0001** 3.null ...
- What every computer science major should know 每一个计算机科学专业的毕业生都应该都知道的
Given the expansive growth in the field, it's become challenging to discern what belongs in a modern ...
- hive如何配置支持事务及insert、update、delete
Hive从0.14版本开始支持事务和行级更新,但缺省是不支持的,需要一些附加的配置.要想支持行级insert.update.delete,需要配置Hive支持事务. 一.Hive具有ACID语义事务的 ...
- javascript控件开发之滚动条控件
首先,基于行前几篇开发的的框架,我们在目录 component\ui\下添加文件 com.ui.scrollBar.js, 在文件中定义com.ui.scrollBar类,继承com.ui.windo ...
- PHPStorm 批量选择,多光标同时编辑相同的内容
一直按Alt+J
- poj 2001 Shortest Prefixes(字典树trie 动态分配内存)
Shortest Prefixes Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 15610 Accepted: 673 ...
- arcgis增大缩放级别
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- 2019-10-18-dotnet-修复找不到-System.ServiceProcess-定义
title author date CreateTime categories dotnet 修复找不到 System.ServiceProcess 定义 lindexi 2019-10-18 21: ...
- 【水滴石穿】github_popular
项目不难,就是文件摆放位置跟别的不一样 https://github.com/chenji336/github_popular //定义入口是app.js ///** @format */ impor ...