1、Django分页器简介
分页功能是几乎所有的网站上都需要提供的功能,当你要展示的条目比较多时,必须进行分页,不但能减小数据库读取数据压力,也有利于用户浏览。
Django又很贴心的为我们提供了一个Paginator分页工具,但是不幸的是,这个工具功能差了点,不好添加CSS样式,所以前端的展示效果比较丑。如果你能力够,自己编写一个分页器,然后提交给Django官方吧,争取替代掉这个当前的分页器,我看好你哦!
但不管怎么样,当前的Paginator分页器,还是值得学一下用一下的。
一、实例展示
向Paginator提供包含一些对象的列表,以及你想每一页显示几条,比如每页5条、10条、20条、100条等等,它就会为你提供访问的一系列API方法,示例如下:
>>> from django.core.paginator import Paginator >>> objects = ['john', 'paul', 'george', 'ringo'] >>> p = Paginator(objects, 2) # 对objects进行分页,虽然objects只是个字符串列表,但没关系,一样用。每页显示2条。 >>> p.count # 对象个数 4 >>> p.num_pages # 总共几页 2 >>> type(p.page_range) # `<type 'rangeiterator'>` in Python 2. <class 'range_iterator'> >>> 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.next_page_number() # 获取下一页的页码 Traceback (most recent call last): ... EmptyPage: That page contains no results >>> page2.previous_page_number() # 获取上一页的页码 1 >>> page2.start_index() # 从1开始计数的当前页的第一个对象 3 >>> page2.end_index() # 从1开始计数的当前页最后1个对象 4 >>> p.page(0) # 访问不存在的页面 Traceback (most recent call last): ... EmptyPage: That page number is less than 1 >>> p.page(3) # 访问不存在的页面 Traceback (most recent call last): ... EmptyPage: That page contains no results
简单地说,使用Paginator分四步走:
- 使用任何方法,获取要展示的对象列表QuerySet;
- 将列表和每页个数传递给Paginator,返回一个分页对象;
- 调用该对象的各种方法,获取各种分页信息;
- 在HTML模板中,使用上面的分页信息构建分页栏。
二、在视图中使用Paginator
下面的例子假设你拥有一个已经导入的Contacts模型。
在视图函数中使用Paginator,参考下面的代码:
视图函数views
from django.shortcuts import render, HttpResponse # Create your views here. from app01.models import * from django.core.paginator import Paginator, EmptyPage def page_test(request): # 在Book表中生成100表记录 # book_list=[] # for i in range(100): # # Book.objects.create(name='book%s'%i,price=10+i) # book=Book(name='book%s'%i,price=10+i) # book_list.append(book) # Book.objects.bulk_create(book_list,10) # 获取当前页码 book_list = Book.objects.all() #从数据库表中获取Book表中的所有记录的queryset对象 paginator = Paginator(book_list, 10) #通过分页器类实例化出一个分页器对象,每页显示10条记录 # print(book_list) #queryset对象, # print(paginator.count) #对象个数 100 # print(paginator.num_pages) #总共分了多少页(总页码数) 10 # print(paginator.page_range) #分页范围 range(1, 11) try: # http://127.0.0.1:8000/page_test/?page=1 # 用户输入url中page值可能不为整数或者是超过我们分页数的范围,我们应该捕获异常,然后让其跳到第一页 current_page = int(request.GET.get('page', 1)) #获取请求的page值,默认是page值是1,得到的是字符串类型 # print(type(request.GET.get('page', 1))) # <class 'str'> # print(current_page) #默认是1,当我们请求不同的页面,就会显示当前是第一页的值 # 生成page对象,传页码,会生成对应页码数据 page = paginator.page(current_page) #调用分页器对象下的page方法,看源码直到其返回的是一个Page类 # print(page) #<Page 1 of 10> ,当点击前端页面的分页数字也会跟着变化,点第五页变为<Page 5 of 10> except Exception: #捕获万能异常类型,当捕获到异常就会走except下的内容 current_page = 1 #如果用户输入的符合法,让当前页强制为第一页 page = paginator.page(1)# # for i in page.object_list: #获取当前页的对象是一个列表,可以循环取出 # print(i) # for i in page: # # 当前页码的每条数据 # print(i) # 是否有下一页 # print(page.has_next()) # # 是否有上一页 # print(page.has_previous()) # # 下一页页码 # print(page.next_page_number()) # # 上一页页码 # print(page.previous_page_number()) #######################################重点############################################## # 显示前5 后5 总共11页---------核心判断分页面的范围,保持一个固定的长度 if paginator.num_pages > 11: if current_page - 5 < 1: page_range = range(1, 12) elif current_page + 5 > paginator.num_pages: page_range = range(paginator.num_pages - 10, paginator.num_pages + 1) else: page_range = range(current_page - 5, current_page + 6) else: page_range = paginator.page_range # 通过locals()可以将后端的数据渲染到后端 return render(request, 'page_test.html', locals()) #######################################重点##############################################
template模板中page_test.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"> <title>Title</title> </head> <body> <ul> <!--#################################显示页面内容####################################--> <!--视图函数从数据库中拿到数据,然后返回渲染到前端页面,通过for循环就可以拿到不同也的所有记录条数--> {% for book in page %} <li>{{ book.name }}</li> {% endfor %} </ul> <!--#################################显示页面内容####################################--> <!--引入bootstrap样式--> <nav aria-label="Page navigation"> <ul class="pagination"> <!--判断拿到的页面是否有上一页,有上一页则可点上一页将上一页的记录显示出来--> {% if page.has_previous %} <li> <!--拿到上一页的页码,这样点击上一页就可以到上一页看到上一页的记录--> <a href="?page={{ page.previous_page_number }}" aria-label="Previous"> <span aria-hidden="true">上一页</span> </a> </li> {% else %} <li class="disabled"> <a href="" aria-label="Previous"> <span aria-hidden="true">上一页</span> </a> </li> {% endif %} <!--循环出也难范围的每一页--> {% for foo in page_range %} {% if current_page == foo %} <!--如果是当前页,我们就就让其跳到foo页面,类active可以来标识当前页--> <li class="active"><a href="?page={{ foo }}">{{ foo }}</a></li> {% else %} <li><a href="?page={{ foo }}">{{ foo }}</a></li> <!--不是当前页则不让其显示蓝色标识--> {% endif %} {% endfor %} {% if page.has_next %} <li><a href="?page={{ page.next_page_number }}" aria-label="Next"><span aria-hidden="true">下一页</span></a> </li> {% else %} <li class="disabled"><a href="" aria-label="Next"><span aria-hidden="true">下一页</span></a></li> {% endif %} </ul> </nav> </body> </html>
模型models生成Book表
from django.db import models # Create your models here. class Book(models.Model): nid=models.AutoField(primary_key=True) name=models.CharField(max_length=32) price=models.DecimalField(max_digits=5,decimal_places=2) def __str__(self): return self.name
分页效果图:
三、Paginator对象
Paginator类拥有以下方法和属性:
方法:
Paginator.page(number)[source]
返回指定页面的对象列表,比如第7页的所有内容,下标以1开始。如果提供的页码不存在,抛出InvalidPage异常。
属性:
- Paginator.count:所有页面的对象总数。
- Paginator.num_pages:页面总数。
- Paginator.page_range:基于1的页数范围迭代器。
四、处理异常
在实际使用中,可能恶意也可能不小心,用户请求的页面,可能千奇百怪。正常我们希望是个合法的1,2,3之类,但请求的可能是‘apple’,‘1000000’,‘#’,这就有可能导致异常,需要特别处理。Django为我们内置了下面几个,Paginator相关异常。
- exception InvalidPage[source]:异常的基类,当paginator传入一个无效的页码时抛出。
- exception PageNotAnInteger[source]:当向page()提供一个不是整数的值时抛出。
- exception EmptyPage[source]:当向page()提供一个有效值,但是那个页面上没有任何对象时抛出。
后面两个异常都是InvalidPage的子类,所以你可以通过简单的except InvalidPage来处理它们。
五、Page对象
Paginator.page()将返回一个Page对象,我们主要的操作都是基于Page对象的,它具有下面的方法和属性:
方法:
- Page.has_next()[source]:如果有下一页,则返回True。
- Page.has_previous()[source]:如果有上一页,返回 True。
- Page.has_other_pages()[source]:如果有上一页或下一页,返回True。
- Page.next_page_number()[source]:返回下一页的页码。如果下一页不存在,抛出InvalidPage异常。
- Page.previous_page_number()[source]:返回上一页的页码。如果上一页不存在,抛出InvalidPage异常。
Page.start_index()[source]
:返回当前页上的第一个对象,相对于分页列表的所有对象的序号,从1开始计数。 比如,将五个对象的列表分为每页两个对象,第二页的start_index()
会返回3。Page.end_index()[source]
:返回当前页上的最后一个对象,相对于分页列表的所有对象的序号,从1开始。 比如,将五个对象的列表分为每页两个对象,第二页的end_index()
会返回4。
属性:
- Page.object_list:当前页上所有对象的列表。
- Page.number:当前页的序号,从1开始计数。
- Page.paginator:当前Page对象所属的Paginator对象。