Django搭建个人博客:文章分页

2259阅读 · 12评论 · 2018/12/20发布   前往评论

随着时间的推移(加上勤奋的写作!),你的博客文章一定会越来越多。如果不进行处理,可能同一个页面会挤上成百上千的文章,不美观不说,还降低了页面的反应速度。

这个时候就需要对文章进行分页的处理。

利用轮子

写一个完善的分页功能是有些难度的,好在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.pydef 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">
                    &laquo; 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 }} &raquo;
                </a>
            {% endif %}
        </span>
    </div>
</div>

...

内容也比较简单,用到了前面的Shell中演示的部分方法,判断当前页所处的位置。

这样就行了!补充几篇文章(笔者共6篇),方便测试。刷新页面后是这样的:

视图中设置了每页只有1篇文章,所以就真的只有1篇了。

当然这只是为了测试,实际环境中肯定要远大于1篇的。

点击第2页的按钮后是这样的:

看到顶部地址栏中的变化了吗?

思考一下page是如何从模板传递到视图的。

总结

除模板外,我们只写了4行代码,就有了还不错的分页导航,Django就是这么贴心。

除了对文章列表,你可以对任何你想分页的地方运用此模块(比如以后要讲到的评论),满足用户各类的需求。

读者还可以稍加阅读Bootstrap 4官方文档,改写一个符合自己品味的外观。






<< 上一章 下一章 >>
本文作者: 杜赛
发布时间: 2018年12月20日 - 21:52
最后更新: 2019年01月28日 - 12:59
知识共享许可协议   转载请保留原文链接及作者



登录 后回复

共有12条评论

avatar
yenava 么么哒! 1

有个地方不明白,为什么请求的是article_list网页,但是传的page参数是在article_detail后面加呢?这个page参数是如何传递的呢?

urlpatterns = [
    path('article-list/', views.article_list, name='article_list'),
    path('article-detail/?page=<int:id>/', views.article_detail, name='article_detail'),
    path('article-create/', views.article_create, name='article_create'),
    path('article-delete/<int:id>/', views.article_delete, name='article_delete'),
    path('article-update/<int:id>/', views.article_update, name='article_update'),
]

 

5个月前 回复


avatar
杜赛 [博主] yenava 么么哒! 1

没太了解您的意思。教程中没有把page参数加在article_detail后面啊?

你的这几行代码是输入错了吗,查询参数应该是无法像这样传递到视图中去的

5个月前 回复


avatar
waynecanfly 么么哒! 1

博主你好,想问你在这篇博客中,shell部分的代码是怎么通过Markdown语法着色的呢?

3个月前 回复


avatar
杜赛 [博主] waynecanfly 么么哒! 1

写太久有点忘记了。

好像是语言标记的python、bash、javascript其中某一个吧~

3个月前 回复


avatar
waynecanfly 杜赛 [博主] 么么哒! 1

好的,找到了是python,谢谢博主

3个月前 回复


avatar
ac1864 么么哒! 1

过关。

这个分页还挺好看的。

2个月前 回复


avatar
sys0613 么么哒! 1

博主您好,我想问下,你的网站是部署在阿里云么?好像只能选择云服务器ESC产品呢,别的都不支持吧?想部署一个玩玩,发现好贵啊

1个月前 回复


avatar
杜赛 [博主] sys0613 么么哒! 1

我是部署在阿里云的,阿里云是几家大厂里最贵的。你也可以选择他家的轻量级服务器,对小博客来说基本够用了。

学生服务器要便宜得多。或者用别家的,要便宜些。

1个月前 回复


avatar
sys0613 么么哒! 1

您好,博主,按照本章节修改以后,启动网站,第一次访问时候,是通过直接访问/article/article-list/,

进入到/article/article-list/这个地址的,就是说还是用前几章的办法。

第一次传递时候并没有get page这个参数,为啥第一次也正常呢。page这个参数默认是1么?

如果我们点击页码2,地址栏会变为/article/article-list/?page=2这个说明传递page=2了,但第一次开开网页时候,page的值并没有传递啊。

-----------------------

我打印了一下page的值,如果访问/article/article-list地址,page=None

也就是说,当page=None时候下面代码,默认返回第一页的内容。只能这么理解了。

articles=paginator.get_page(page)

1个月前 回复


avatar
杜赛 [博主] sys0613 么么哒! 1

你的理解是对的,默认返回第一页的内容

1个月前 回复


avatar
Max 么么哒! 1
本回复已被 Max 删除

28天前 回复


avatar
ligang123 么么哒! 0
本回复已被 ligang123 删除

58分钟前 回复