共计 2481 个字符,预计需要花费 7 分钟才能阅读完成。
随着时间的推移(加上勤奋的写作!),你的博客文章一定会越来越多。如果不进行处理,可能同一个页面会挤上成百上千的文章,不美观不说,还降低了页面的反应速度。
这个时候就需要对文章进行分页的处理。
利用轮子
写一个完善的分页功能是有些难度的,好在 Django 已经帮你准备好一个现成的分页模块了(Django 把大部分基础功能都替你准备好了!)。内置模块虽然简单,但是对博客来说完全足够了。
我们要用到的是 Paginator 类。在 Shell 中可以充分尝试它的用法:
>>> from django.core.paginator import Paginator
>>> objects = [‘john’, ‘paul’, ‘george’, ‘ringo’]
>>> p = Paginator(objects, 2)
>>> p.count
4
>>> p.num_pages
2
>>> p.page_range
range(1, 3)
>>> page1 = p.page(1)
>>> page1
<Page 1 of 2>
>>> page1.object_list
[‘john’, ‘paul’]
>>> page2 = p.page(2)
>>> page2.object_list
[‘george’, ‘ringo’]
>>> page2.has_next()
False
>>> page2.has_previous()
True
>>> page2.has_other_pages()
True
>>> page2.previous_page_number()
1
这是一个官网的例子。详见:Pagination
有了这个类,剩下的工作就是把它应用到项目中去。
轻车熟路
要对文章列表分页,因此就要修改 article/views.py 的 def article_list() 视图:
article/views.py
…
# 引入分页模块
from django.core.paginator import Paginator
def article_list(request):
# 修改变量名称(articles -> article_list)
article_list = ArticlePost.objects.all()
# 每页显示 1 篇文章
paginator = Paginator(article_list, 1)
# 获取 url 中的页码
page = request.GET.get(‘page’)
# 将导航对象相应的页码内容返回给 articles
articles = paginator.get_page(page)
context = {‘articles’: articles}
return render(request, ‘article/list.html’, context)
…
在视图中通过 Paginator 类,给传递给模板的内容做了手脚:返回的不再是所有文章的集合,而是对应页码的部分文章的对象,并且这个对象还包含了分页的方法。
我们在前面的文章已经接触过一些将参数传递到视图的手段了:
通过 POST 请求将表单数据传递到视图
通过 url 将地址中的参数传递到视图
这里用到了另一种方法:在 GET 请求中,在 url 的末尾附上?key=value 的键值对,视图中就可以通过 request.GET.get(‘key’) 来查询 value 的值。
然后改写模板,在最末尾的 </div> 前面,加入分页的内容:
templates/article/list.html
…
<!– 页码导航 –>
<div class=”pagination row”>
<div class=”m-auto”>
<span class=”step-links”>
<!– 如果不是第一页,则显示上翻按钮 –>
{% if articles.has_previous %}
<a href=”?page=1″ class=”btn btn-success”>
« 1
</a>
<span>…</span>
<a href=”?page={{articles.previous_page_number}}”
class=”btn btn-secondary”
>
{{articles.previous_page_number}}
</a>
{% endif %}
<!– 当前页面 –>
<span class=”current btn btn-danger btn-lg”>
{{articles.number}}
</span>
<!– 如果不是最末页,则显示下翻按钮 –>
{% if articles.has_next %}
<a href=”?page={{articles.next_page_number}}”
class=”btn btn-secondary”
>
{{articles.next_page_number}}
</a>
<span>…</span>
<a href=”?page={{articles.paginator.num_pages}}”
class=”btn btn-success”
>
{{articles.paginator.num_pages}} »
</a>
{% endif %}
</span>
</div>
</div>
…
内容也比较简单,用到了前面的 Shell 中演示的部分方法,判断当前页所处的位置。
这样就行了!补充几篇文章(笔者共 6 篇),方便测试。刷新页面后是这样的:
视图中设置了每页只有 1 篇文章,所以就真的只有 1 篇了。
当然这只是为了测试,实际环境中肯定要远大于 1 篇的。
点击第 2 页的按钮后是这样的:
看到顶部地址栏中的变化了吗?
思考一下 page 是如何从模板传递到视图的。
总结
除模板外,我们只写了 4 行代码,就有了还不错的分页导航,Django 就是这么贴心。
除了对文章列表,你可以对任何你想分页的地方运用此模块(比如以后要讲到的评论),满足用户各类的需求。
读者还可以稍加阅读 Bootstrap 4 官方文档,改写一个符合自己品味的外观。
有疑问请在杜赛的个人网站留言,我会尽快回复。
或 Email 私信我:dusaiphoto@foxmail.com
项目完整代码:Django_blog_tutorial
转载请并注明出处。