使用 Django Pagination 实现简单的分页功能

当网页上显示的数据过多时,通常需要进行分页显示。Django 内置的 Pagination 能够帮助我们实现简单的分页功能。

Paginator 类的常用方法

分页功能由 Django 内置的 Paginator 类提供。这个类位于 django/core/paginator.py,需要使用它时,只需在适当的地方导入这个类即可。

from django.core.paginator import Paginator

只需实例化一个 Paginator 对象,并在实例化时传入一个需要分页的对象列表,就可以得到分页后的对象数据。

# 对 item_list 进行分页,每页包含 2 个数据。
>>> item_list = ['john', 'paul', 'george', 'ringo']
>>> p = Paginator(item_list, 2)

取特定页的数据:

# 取第 2 页的数据
>>> page2 = p.page(2)
>>> page2.object_list
['george', 'ringo']

查询特定页的当前页码数:

>>> page2.number
2

查看分页后的总页数:

>>> p.num_pages
2

查看某一页是否还有上一页,以及查询该页上一页的页码:

# 查询第二页是否还有上一页
>>> page2.has_previous()
True

# 查询第二页上一页的页码
>>> page2.previous_page_number()
1

查看某一页是否还有下一页,以及查询该页下一页的页码:

# 查询第二页是否还有下一页
>>> page2.has_next()
False

# 查询第二页下一页的页码
>>> page2.next_page_number()
django.core.paginator.EmptyPage: That page contains no results

更多方法和属性请参阅 Django Pagination 的官方文档

用 Paginator 给文章列表分页

使用上面的一些方法,我们可以实现一个类似于 Django 官方博客一样的简单分页效果,效果如下。

Django官方博客简单分页效果

假设我们在博客的首页展示全部文章,其对应的视图函数为:

def index(request):
    post_list = Post.objects.all()
    return render(request, 'blog/index.html', context={'post_list': post_list})

这里我们把全部文章取出来后存在 post_list 变量里,然后将它传递给了模板。现在来使用 Paginator 类对 post_list 进行分页。在视图函数里不再将全部的文章数据 post_list 传给模板了,而是把用户请求页的数据传给模板,这样用户看到的就是其请求页的文章数据。

视图函数修改如下:

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

def index(request):
    post_list = Post.objects.all() 
    paginator = Paginator(post_list, 10) 
    page = request.GET.get('page') 

    try:
        post_list = paginator.page(page) 
    except PageNotAnInteger:
        post_list = paginator.page(1) 
    except EmptyPage:
        post_list = paginator.page(paginator.num_pages) 

    return render(request, 'blog/index.html', context={'post_list': post_list})

① 获取全部的文章数据,保存在 post_list 中。

② 对 post_list 进行分页,每页 10 篇文章。为了测试分页你可以把数字改小点。

③ 获取用户请求页的页码。我们给页码设置的 URL 类似于 http://zmrenwu.com/?page=2。其中 ? 号后面的 page=2 表示用户请求的页码数。Django 会将问号后面的请求参数保存到 request.GET 属性里,这是一个类字典的属性。例如这里 page 作为键被保存,其值为 2。

④ 尝试获取用户请求页的文章列表。

⑤ 用户请求的 URL 中,page 的值可能不一定是整数,例如用户可能请求 http://zmrenwu.com/?page=xyz 这样的 URL。这时候将 page 作为参数传给 paginator.page 方法将抛出一个 PageNotAnInteger 异常。我们处理这个异常的方式是:将第一页的数据返回给用户。

⑥ 如果 page 的值是一个整数,但是值太大了。例如总共只有 4 页,但用户请求第 10 页的数据,这时候 paginator.page 方法会抛出 EmptyPage 异常。这里处理这个异常的方式是:返回最后一页的数据给用户。

在模板中设置分页导航

接下来便是在模板中设置分页导航,比如上一页、下一页的按钮,以及显示一些页面信息。我们这里设置和 Django 官方博客那样的分页导航样式(具体的样式见上图)。

在你想要显示分页信息的地方使用下面的代码。这里以 index.html 为例:

<div class="pagination">
  {% if post_list.has_previous %}
    <!-- 如果当前页还有上一页,显示一个上一页的按钮 -->
    <a href="?page={{ post_list.previous_page_number }}">上一页</a>
  {% endif %}
  <span class="current">
    <!-- 显示当前页面信息 -->
    第 {{ post_list.number }} 页 / 共 {{ post_list.paginator.num_pages }} 页
  </span>
  {% if post_list.has_next %}
    <!-- 如果当前页还有上下页,显示一个下一页的按钮 -->
    <a href="?page={{ post_list.next_page_number }}">下一页</a>
  {% endif %}
</div>

其中 {{ }} 模板变量中的内容,其含义已在文章开头部分的Paginator 类的常用方法中已有介绍。最终我们得到如下的分页效果:

Django博客演示项目分页效果图

当然这只是一个简单示例,分页导航处的视觉效果并不是很好看,你可以自行为其添加 CSS 样式使其看上去更加美观。

进一步拓展

使用 Django 内置的 Pagination 只能实现上面的简单分页效果,但通常更加高级的分页效果应该像下图这样:

高级分页效果图

当前页面高亮显示,且显示当前页面前后几页的页码,始终显示第一页和最后一页的页码,中间可能还有省略号的效果,表示还有未显示的页码。

仅仅使用 Django Pagination 内置的方法无法实现这样的效果,需要我们写一些额外的代码来拓展 Pagination 的功能。下一篇文章将详细说明该如何拓展 Pagination 以实现一个完善的分页效果。

-- EOF --


0 条评论 / 0 人参与