扩展Django中的分页
Django中封装了分页模块,定义了两个类分别是Paginator和Page。虽然可以满足一般的需求,但是稍想添加点新的功能就显得鸡肋,而且创建paginator类对象时需要传人所有的数据对象(由于django的惰性查询所以适用django),没有可移植性。
下面先在原分类模块基础上另外封装两个功能,分别实现设置页面最多显示页码数 和切换页码时保留原搜索条件。
一、设置页面最多显示页码数
设置最多页码数后,页面显示的页码和总页码数还有当前页码有关,封装一个类继承自Paginator,定义pager_num_range方法如下:
1 from django.core.paginator import Paginator 2 3 class CustomPaginator(Paginator): 4 5 def __init__(self,current_page,per_pager_number,*args, **kwargs): 6 # 当前页 7 self.current_page = int(current_page) 8 # 页面最多显示的页码数 9 self.per_pager_number = int(per_pager_number) 10 self.per_pager_number = int(per_pager_number) 11 super(CustomPaginator,self).__init__(*args, **kwargs) 12 13 def pager_num_range(self): 14 # 页码最多显示的页码数的一半 15 part = self.per_pager_number // 2 16 # 总页数小于最多显示的页码数 17 if self.num_pages < self.per_pager_number: 18 return range(1, self.num_pages+1) 19 # 当前页小于最多显示页码数的一半 20 elif self.current_page <= part: 21 return range(1, self.per_pager_number+1) 22 # 当前页在最后的几页(从最多显示页码的一半往后几页) 23 elif self.num_pages - self.current_page <= part: 24 return range(self.num_pages-self.per_pager_number+1,self.num_pages+1) 25 # 其余情况 26 else: 27 if self.per_pager_number % 2 == 0: 28 return range(self.current_page-part+1, self.current_page+part+1) 29 else: 30 return range(self.current_page-part, self.current_page+part+1)
然后创建paginator类对象是通过CustomPaginator类创建即可,传入必要的参数
paginator = CustomPaginator(current_page,11,USER_LIST,10)
最后创建page类对象,渲染模板即可,模板中page类对象调用paginator.pager_numrange方法即可获取设置最多显示页码数后的页码列表,简单配置一下
{% for i in posts.paginator.pager_num_range %} {% if i == posts.number %} <a style="font-size: 30px">{{ i }}</a> {% else %} <a href="/index1.html?p={{ i }}">{{ i }}</a> {% endif %} {% endfor %}
二、切换页码时保留搜索条件
切换页码保留搜索条件,需要先获取请求url中的所有参数,然后在操作上一页、下一页、点击页码时只需修改url参数中对应的页码,将修改后的url参数设置给page类对象的属性 传给前端即可。
1 from django.core.paginator import Paginator 2 3 def index3(request): 4 paginator = Paginator(USER_LIST, 10) 5 data = request.GET 6 current_page = data.get('p') 7 posts = paginator.page(current_page) 8 9 """修改页码支持保留搜索条件""" 10 import copy 11 data = copy.deepcopy(data) 12 13 # 总页数 14 all_num, b = divmod(len(USER_LIST), 10) 15 if b != 0: 16 all_num += 1 17 18 # 传入的页码值有误 19 try: 20 current_page = int(current_page) 21 except Exception as e: 22 current_page = 1 23 24 # 传入的页码值大于总页数 25 if current_page >= all_num: 26 current_page = all_num 27 28 # 上一页 29 data['p'] = current_page - 1 30 posts.previous_page = "%s" % data.urlencode() 31 32 # 下一页 33 data['p'] = current_page + 1 34 posts.next_page = "%s" % data.urlencode() 35 36 # 页面显示的页码 37 # 每一页需要分别保存url参数和页码值 38 posts.num_li = [] 39 for i in range(1,all_num+1): 40 data['p'] = i 41 # url参数 页码值 42 posts.num_li.append({"data":data.urlencode(),"p":i}) 43 44 return render(request, 'index3.html',{"posts":posts})
然后在模板中分别通过page对象的对应属性 获取对应的url参数拼接到url中即可
{% if posts.has_previous %} <a href="/index3.html?{{ posts.previous_page }}">上一页</a> {% else %} <a>上一页</a> {% endif %} {% for i in posts.num_li %} {% if i.p == posts.number %} <a style="font-size: 30px" href="/index3.html?{{ i.data }}">{{ i.p }}</a> {% else %} <a href="/index3.html?{{ i.data }}">{{ i.p }}</a> {% endif %} {% endfor %} {% if posts.has_next %} <a href="/index3.html?{{ posts.next_page }}">下一页</a> {% else %} <a>下一页</a> {% endif %}
效果:
三、自定义分页组件
最开始说过了,django的分页模块由于django对数据库的惰性查询才可以适用,在别的框架或用途中是不能够使用的,因为它需要先将需要的所有数据从数据库中查询出来,这样肯定是不对的。
自己封装一个Paginator类,传入四个参数:所有数据的个数、当前页码值、每页显示多少条 和 页面显示的最多页码数。
封装方法有:
start()、end():第几页的数据 起始切片和结束切片
num_pages():总页数,用property属性将其设置属性
pager_num_range():获取页面显示的页码
pager_str():页面所有与页码相关的按钮html代码
思路:起始切片和结束切片没什么好说的了,总页数通过python的内置函数divmod()可以得到,页面显示的页码上面写过了,和页码相关的按钮html代码这个的用意是 模板中只需要写上创建的paginator对象.pager_str,那么和页码相关的就都完事了。
做法就是 和 切换页面保留搜索条件一样 拼接url参数。我这里直接写死了url前面部分,可以通过给方法添加一个参数来改变url前面部分,这样更通用点。
下面上代码:
1 class Paginator(object): 2 3 def __init__(self, totalCount,currentPage,request,perPageItemNum=10,maxPageNum=7): 4 """ 5 初始化 6 :param totalCount: 所有数据的个数 7 :param currentPage: 当前页 8 :param perPageItemNum:每页显示多少条 9 :param maxPageNum:页面显示的最多页码数 10 """ 11 self.total_count = totalCount 12 try: 13 v = int(currentPage) 14 if v <= 0: 15 v = 1 16 self.current_page = v 17 except Exception as e: 18 self.current_page = 1 19 self.per_page_item_num = perPageItemNum 20 self.max_page_num = maxPageNum 21 22 # 获取url中的筛选条件:<QueryDict:{key: value}> 23 params = request.GET 24 import copy 25 self.new_params = copy.deepcopy(params) 26 27 def start(self): 28 # 起始切片 29 return (self.current_page-1)*self.per_page_item_num 30 31 def end(self): 32 # 结束切片 33 return self.current_page*self.per_page_item_num 34 35 @property 36 def num_pages(self): 37 """总页数""" 38 a, b = divmod(self.total_count, self.per_page_item_num) 39 if b != 0: 40 a += 1 41 return a 42 43 def pager_num_range(self): 44 # 页码最多显示的页码数的一半 45 part = self.max_page_num // 2 46 # 总页数小于最多显示的页码数 47 if self.num_pages < self.max_page_num: 48 return range(1, self.num_pages+1) 49 # 当前页小于最多显示页码数的一半 50 elif self.current_page <= part: 51 return range(1, self.max_page_num+1) 52 # 当前页在最后的几页(从最多显示页码的一半往后几页) 53 elif self.num_pages - self.current_page <= part: 54 return range(self.num_pages-self.max_page_num+1,self.num_pages+1) 55 # 其余情况 56 else: 57 if self.max_page_num % 2 == 0: 58 return range(self.current_page-part+1, self.current_page+part+1) 59 else: 60 return range(self.current_page-part, self.current_page+part+1) 61 62 def page_str(self): 63 page_list = [] 64 65 # 首页 66 self.new_params['p'] = 1 67 first = '<li><a href="/index2.html?%s">首页</a></li>' % self.new_params.urlencode() 68 page_list.append(first) 69 70 # 上一页 71 if self.current_page == 1: 72 prev = '<li><a>上一页</a><>/li' 73 else: 74 self.new_params['p'] = self.current_page-1 75 prev = '<li><a href="/index2.html?%s">上一页</a></li>' % (self.new_params.urlencode()) 76 page_list.append(prev) 77 78 # 页面页码 79 for i in self.pager_num_range(): 80 self.new_params['p'] = i 81 if self.current_page == i: 82 temp = '<li class="active"><a href="/index2.html?%s">%s</a>' % (self.new_params.urlencode(),i) 83 else: 84 temp = '<li><a href="/index2.html?%s">%s</a></li>' % (self.new_params.urlencode(), i) 85 page_list.append(temp) 86 87 # 下一页 88 if self.current_page >= self.num_pages: 89 next = '<li><a>下一页</a></li>' 90 else: 91 self.new_params['p'] = self.current_page+1 92 next = '<li><a href="/index2.html?%s">下一页</a></li>' % (self.new_params.urlencode()) 93 page_list.append(next) 94 95 # 尾页 96 self.new_params['p'] = self.num_pages 97 last = '<li><a href="/index2.html?%s">尾页</a></li>' % self.new_params.urlencode() 98 page_list.append(last) 99 100 return ' '.join(page_list)
然后视图中创建paginator类对象,渲染模板即可。模板中 禁止转义
def index2(request): from app01.pager import Paginator current_page = request.GET.get('p') obj = Paginator(666,current_page,request) data_list = USER_LIST[obj.start():obj.end()] return render(request, 'index2.html',{"data_list":data_list,"page_obj":obj})
{{ page_obj.page_str | safe }}
最后用bootstarp美观了一下,效果: