这一篇博客记录一下自己学习Django中分页功能的笔记。分页功能在每个网站都是必要的,当页面因需要展示的数据条目过多,导致无法全部显示,这时候就需要采用分页的形式进行展示。
分页在网站随处可见,下面展示一个分页的样式:
分页的实现,不仅提高了用户的体验,还减轻了数据库读取数据的压力。Django自带名为Paginator的分页工具,方便我们实现分页功能,这个类存放在django/core/paginator.py。它可以接收列表,元组或者其他可迭代对象。
下面先学习一下Paginator的基本语法。
Django中Paginator基本语法
1,分页器函数Paginator的基本语法
Paginator类的作用是将我们需要分页的数据分割成若干份,当我们实现一个Paginator类的实例时,需要给其传入参数,我们点到Paginator类里,可以看到其定义如下:
class Paginator:
def __init__(self, object_list, per_page, orphans=0,
allow_empty_first_page=True):
self.object_list = object_list
self._check_object_list_is_ordered()
self.per_page = int(per_page)
self.orphans = int(orphans)
self.allow_empty_first_page = allow_empty_first_page
根据定义我们可以做如下解释,(上述代码没有将其类属性和方法贴出来):
object_list:可以是列表,元组,查询集或者其他含有count()或者 __len()__方法的可切片对象。对于连续的分页,查询集应该有序,例如有order_by()项或者默认ordering参数。
per_page:每一页中包含条目数目的最大值,不包括独立成页的那页。
orphans=0:当你使用此参数时说明你不希望最后一页只有很少的条目。如果最后一页的条目数少于等于orphans的值,则这些条目会被归并到上一页中(此时的上一页变成最后一页)。例如有23项条目,per_page=10,orphans=0,则由三页,分别为10,10,3,如果orphans>=3,则为2页,分别为10, 13。
allow_empty_first_page=True:表示默认允许第一页为空
一般情况,我们只需传入两个参数。第一个参数是数据源,可以是列表,元组,以及查询集。第二个参数需要传入一个整数,表示每页显示数据条数。
1.1 Paginator类的方法
Paginator.page(number):根据参数number返回一个Page对象(number为1的倍数)
使用如下:
#第1页的page对象
page1=paginator.page(1)
for i in page1: #遍历第1页的所有数据对象
print(i)
print(page1.object_list) #第1页的所有数据
#第2页的page对象
page2=paginator.page(2)
1.2 Paginator类的属性
Paginator.count:所有页面对象总数,即统计 object_list 中 item 数目,当计算 object_list 所含对象的数量时,Paginator 会首先尝试调用 object_list.count()。如果 object_list没有 count()方法,Paginator接着会回退使用 Len(object_list)。
Paginator.num_pages:页面总数
Paginator.page_range:页码范围(页码列表),从1开始(列表是顾头不顾尾),例如[1,2,3,4]。
上面三个属性是我们Paginator类中常用的属性。我们可以打印其属性对应的值:
book_list=Book.objects.all()
paginator = Paginator(book_list, 10)
print("count:",paginator.count) #数据总数
print("num_pages",paginator.num_pages) #总页数
print("page_range",paginator.page_range) #页码的列表
2,Page对象的基本语法
我们通常不用手动创建Page对象,可以从Paginator类来获取。Paginator类提供一个 **page(number)** 函数,该函数返回的是一个Page对象。参数number表示第几个分页。如果number=1,那么page() 返回的对象是第一分页的Page对象。在前端页面中显示数据,我们主要的操作都是基于Page对象。具体的定义如下:
class Page(collections.Sequence):
def __init__(self, object_list, number, paginator):
self.object_list = object_list
self.number = number
self.paginator = paginator
2.1 page对象的方法
Page.has_next() 如果有下一页,则返回True。
Page.has_previous()如果有上一页,返回 True。
Page.has_other_pages() 如果有上一页或下一页,返回True。
Page.next_page_number() 返回下一页的页码。如果下一页不存在,抛出InvlidPage异常。
Page.previous_page_number() 返回上一页的页码。如果上一页不存在,抛出InvalidPage异常。
Page.start_index() 返回当前页上的第一个对象,相对于分页列表的所有对象的序号,从1开始。比如,将五个对象的列表分为每页两个对象,第二页的start_index()会返回3。
Page.end_index() 返回当前页上的最后一个对象,相对于分页列表的所有对象的序号,从1开始。 比如,将五个对象的列表分为每页两个对象,第二页的end_index() 会返回 4。
上面加粗的就是我们常用的 page对象方法:
page2=paginator.page(2)
print(page2.has_next()) #是否有下一页
print(page2.next_page_number()) #下一页的页码
print(page2.has_previous()) #是否有上一页
print(page2.previous_page_number()) #上一页的页码
2.2 page对象的属性
Page.object_list当前页上所有对象的列表。
Page.number当前页的序号,从1开始。
Page.paginator相关的Paginator对象
2.3 page对象的用法
下面举个例子:
# 使用 Paginator 对象返回第一页的 page 对象
books = paginator.page(1)
这就是当number=1的时候,page()返回的对象就是第一分页的Page对象。
3,非法页码的处理
InvalidPage(Exception): 异常的基类,当paginator传入一个无效的页码时抛出。
Paginator.page()放回在所请求的页面无效(比如不是一个整数)时,或者不包含任何对象时抛出异常。通常,捕获InvalidPage异常就够了,但是如果你想更加精细一些,可以捕获以下两个异常之一:
exception PageNotAnInteger,当向page()提供一个不是整数的值时抛出。
exception EmptyPage,当向page()提供一个有效值,但是那个页面上没有任何对象时抛出。
这两个异常都是InalidPage的子类,所以可以通过简单的try - except InvalidPage来处理它们。
try:
print(page)
book_list = paginator.page(page)
except PageNotAnInteger:
book_list = paginator.page(1)
except EmptyPage:
book_list = paginator.page(paginator.num_pages)
4,对Paginator类中函数的简单练习
分页是Web应用常用的手法,Django提供了一个分页器类Paginator(django.core.paginator.Paginator),可以很容易的实现分页的功能。
该类有两个构造参数,一个是数据的集合,另一个是每页放多少条数据。
Paginator的基本使用如下:
$python manage.py 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 #页码的列表
[1, 2]
>>> page1 = p.page(1) #第1页
>>> page1
>>> page1.object_list #第1页的数据
['john', 'paul']
>>> page2 = p.page(2)
>>> page2.object_list #第2页的数据
['george', 'ringo']
>>> page2.has_next() #是否有后一页
False
>>> page2.has_previous() #是否有前一页
True
>>> page2.has_other_pages() #是否有其他页
True
>>> page2.next_page_number() #后一页的页码
3
>>> page2.previous_page_number() #前一页的页码
1
>>> page2.start_index() # 本页第一条记录的序数(从1开始)
3
>>> page2.end_index() # 本页最后录一条记录的序数(从1开始)
4
>>> p.page(0) #错误的页,抛出异常
...EmptyPage: That page number is less than 1
>>> p.page(3) #错误的页,抛出异常
...EmptyPage: That page contains no results
其实前面scaffold生成的内容里面就已经包含了分页的功能,相信有了对Paginator的了解,
你自己就可以看懂在view函数和模板中如何使用分页器了。
5, 一个简单的示例
基本代码如下:
from django.shortcuts import render,HttpResponse
# Create your views here.
from django.core.paginator import Paginator,InvalidPage,EmptyPage,PageNotAnInteger
def index(req):
user_list=["用户"+str(i) for i in range(100)]
user_list=getPage(req,user_list)
return render(req,"index.html",locals())
def getPage(req,user_list):
paginator=Paginator(user_list,5)
try:
current_page=req.GET.get("page",1) # http://127.0.0.1:8000/index/?page=20
article_list=paginator.page(current_page)
except (EmptyPage,InvalidPage,PageNotAnInteger):
article_list=paginator.page(1)
return user_list
#*******----------index.html
{% for i in user_list %}
{ { i }}
{% endfor %}
分页功能的制作过程
1,完成分页功能的总体思路
下面分页学习的总体思路分为五步。
1,给数据库插入很多数据,然后保证可以进行分页
2,完成简单的前端分页样式,然后我们可以看到分页的雏形