如果在首页和资料页面显示全部文章,页面加载速度会变慢,而且有点不切实际。在WEB浏览中,内容过多的网页需要花费更多的时间生成、下载和渲染。解决办法是:分页显示数据并分段渲染。
实现博客文章分页,需要大量的数据,手动添加费时费力。Python有多个包可以提供自动化解决方案,用于生成虚拟信息,其中比较完善的是Faker,这个包用pip安装:
pip install faker
Faker包并不是这个应用的依赖,因为它只在开发过程中使用。为了区分生产环境的依赖和开发环境的依赖,我们使用requirements目录取代requirements.txt文件,在该目录下分别创建dev.txt和pro.txt文件;由于2个环境使用的大部分依赖都是相同的,可以再创建一个common.txt,在dev.txt和pro.txt中使用-r参数将其导入即可:
requirements/dev.txt:开发需求文件
-r common.txt
faker==0.7.18
接下来,我们创建一个新模块fake.py,分别生成虚拟用户和博客文章:
from random import randint
from sqlalchemy.exc import IntegrityError
from faker import Faker
from . import db
from .models import User, Post
def users(count=100):
fake = Faker()
i = 0
while i < count:
u = User(email=fake.email(),
username=fake.user_name(),
password='password',
confirmed=True,
name=fake.name(),
location=fake.city(),
about_me=fake.text(),
member_since=fake.past_date())
db.session.add(u)
try:
db.session.commit()
i += 1
except IntegrityError:
db.session.rollback()
def posts(count=100):
fake = Faker()
user_count = User.query.count()
for i in range(count):
u = User.query.offset(randint(0, user_count - 1)).first()
p = Post(body=fake.text(),
timestamp=fake.past_date(),
author=u)
db.session.add(p)
db.session.commit()
Faker包提供的随机信息生成器,可以生成看起来很逼真的姓名,电子邮件地址,句子等等。注意:由于用户的电子邮件地址和用户名必须是唯一的,Faker随机生成这些信息时会有重复的风险,此时提交数据库会话时会抛出IntegrityError异常,此时需要捕获异常,回滚数据库提交会话。
随机生成文章时,要为每篇文章随机指定一个用户,为此,我们使用offset查询过滤器,这个过滤器会跳过参数指定的记录数量。为了每次都得到不同的用户,我们使用randint(0, user_count - 1)设定一个随机的偏移,然后调用first方法。
运行:
为了支持分页,而对首页路由和资料页路由的相关查询对象使用paginate()方法。
app/main/view.py:分页显示博客文章列表
@main.route('/', methods=['GET', 'POST'])
def index():
form = PostForm()
if current_user.can(Permission.WRITE) and form.validate_on_submit():
post = Post(body=form.body.data,
author=current_user._get_current_object())
db.session.add(post)
db.session.commit()
return redirect(url_for('.index'))
page = request.args.get('page', 1, type=int)
pagination = Post.query.order_by(Post.timestamp.desc()).paginate(
page, per_page=current_app.config['FLASKY_POSTS_PER_PAGE'],
error_out=False)
posts = pagination.items
return render_template('index.html', form=form, posts=posts,
pagination=pagination)
渲染的字符串从请求的查询字符串request.args中获取,如果没有明确指定,则默认渲染第一页,参数type=int确保参数在无法转换成整数时返回默认值。
为了显示某页中的记录,Post.query.order_by(Post.timestamp.desc())查询对象不能调用all()方法了,要把all()换成Flask-SQLAlchemy提供的paginate()方法。页数是paginate()方法的首个参数,也是唯一必须参数;可选参数per_page用来指定每页显示的记录数,默认为20;另一个可选参数为error_out,为True(默认)时,如果请求的页数超出了范围,则返回404错误,如果设为False,页面超出范围时显示一个空列表。
app/main/views.py:用户详情页博客文章列表进行分页显示
@main.route('/user/')
def user(username):
user = User.query.filter_by(username=username).first_or_404()
page = request.args.get('page', 1, type=int)
pagination = user.posts.order_by(Post.timestamp.desc()).paginate(
page, per_page=current_app.config['FLASKY_POSTS_PER_PAGE'],
error_out=False)
posts = pagination.items
return render_template('user.html', user=user, posts=posts,
pagination=pagination)
最后记得在config.py中添加新增的配置项:
class Config:
...
FLASKY_POSTS_PER_PAGE = int(os.environ.get('FLASKY_POSTS_PER_PAGE') or '20')
这样修改后,如果想查看第二页的文章,只需在URL后面加上查询字符串?page=2即可: