自定义分页的实现
放在项目的utils包中
自定义分页属于自己写的功能,把它放在项目的utils包中,py文件取名为page.py。
主要看类实现的方法——函数low鸡版不看了
page.py文件中的内容:总得来说就是后台拼接好标签~然后拿到前端显示出来——为每一个标签都加上一个方法就好~~
## 需要特别注意:因为这个CRM项目搜索与信息展示用了同一个视图函数~因此在分页中做了“保存搜索条件”的处理——保存搜索条件:就是搜索完成后,第一页显示的是搜索的结果,但是点击第二页如果不做处理的话第二页会显示“所有结果的第二页”,因此需要在分页这里做一下处理!
#自定义分页 #官方推荐,页码数为奇数 class PageNation: def __init__(self,base_url,current_page_num,total_counts,request,per_page_counts=10,page_number=5,): ''' :param base_url: 分页展示信息的基础路径 :param current_page_num: 当前页页码 :param total_counts: 总的数据量 :param per_page_counts: 每页展示的数据量 :param page_number: 显示页码数 ''' self.base_url = base_url self.current_page_num = current_page_num self.total_counts = total_counts self.per_page_counts = per_page_counts self.page_number = page_number self.request = request try: self.current_page_num = int(self.current_page_num) except Exception: self.current_page_num = 1 half_page_range = self.page_number // 2 # 计算总页数~~divmod方法返回商跟余数! self.page_number_count, a = divmod(self.total_counts, self.per_page_counts) if self.current_page_num < 1: self.current_page_num = 1 #有余数的话,说明还有一页有遗留的数据,需要给总的页数加1 if a: self.page_number_count += 1 ######################################################## 当前页码为0的话~会有问题,下面这样的标记有做处理! if self.current_page_num > self.page_number_count: self.current_page_num = self.page_number_count if self.page_number_count <= self.page_number: self.page_start = 1 self.page_end = self.page_number_count else: # 当前页小于定制的页数的一半的话~就是前面的几页,前面只显示到第一条数据就好了! if self.current_page_num <= half_page_range: self.page_start = 1 self.page_end = page_number # 当前页大于定制的页数的一半的话~就是后面的几页,后面顶头了,最后只显示最后一条的数据就好了! elif self.current_page_num + half_page_range >= self.page_number_count: # 起始页码等于~总页数减去显示的页码数+1~~不加1的话会多添加一个标签! self.page_start = self.page_number_count - self.page_number + 1 self.page_end = self.page_number_count else: self.page_start = self.current_page_num - half_page_range self.page_end = self.current_page_num + half_page_range ## 下面的逻辑是:搜索时保存搜索条件的逻辑!!! ## 后面的 循环生成html标签 会用到urlencode()方法!注意后面格式化标签字符串的方法! import copy # request.GET['page'] = 2 #This QueryDict instance is immutable from django.http.request import QueryDict # print(type(request)) # print(request) # print(request.GET) # request.GET方法返回的是一个QueryDict类型的数据, # 它有一个urlencode()方法,可以将它里面的键值对转换成 ?condition=qq&wd=1&page=3的格式! # 这样的格式发送GET请求后浏览器能认识 # 深拷贝后可以改了,避免浏览器请求的地址堆叠 # 因为深拷贝调用了__deepcopy__方法,看QueryDict的源码可知这个方法把不可变的那个属性又改成了True使之可变 # (一开始_mutable为True,init方法mutable设置成False......这个过程得知道!) self.params = copy.deepcopy(self.request.GET) # params['page'] = current_page_num # query_str = params.urlencode() #数据切片依据,起始位置 @property def start_num(self): ################################################ 这里有个问题:如果没有数据的话,必须给 self.current_page_num = 1~~ ################################################ if self.current_page_num == 0: self.current_page_num = 1 start_num = (self.current_page_num - 1) * self.per_page_counts return start_num #数据切片依据,终止位置 @property def end_num(self): end_num = self.current_page_num * self.per_page_counts return end_num # 拼接HTMl标签 def page_html(self): tab_html = '' tab_html += '<nav aria-label="Page navigation" class="pull-right"><ul class="pagination">' # 首页 self.params['page'] = 1 showye = '<li><a href="{0}?{1}" aria-label="Previous" ><span aria-hidden="true">首页</span></a></li>'.format( self.base_url, self.params.urlencode()) tab_html += showye # 上一页 if self.current_page_num == 1: previous_page = '<li disabled><a href="#" aria-label="Previous" ><span aria-hidden="true">«</span></a></li>' else: previous_page = '<li><a href="{0}?page={1}" aria-label="Previous" ><span aria-hidden="true">«</span></a></li>'.format( self.base_url, self.current_page_num - 1) tab_html += previous_page ## 循环生成页码标签! for i in range(self.page_start, self.page_end + 1): self.params['page'] = i if self.current_page_num == i: one_tag = '<li class="active"><a href="{0}?{2}">{1}</a></li>'.format(self.base_url, i,self.params.urlencode()) #?condition=qq&wd=1&page=3 else: one_tag = '<li><a href="{0}?{2}">{1}</a></li>'.format(self.base_url, i,self.params.urlencode()) tab_html += one_tag # 下一页 if self.current_page_num == self.page_number_count: next_page = '<li disabled><a href="#" aria-label="Next"><span aria-hidden="true">»</span></a></li>' else: next_page = '<li><a href="{0}?page={1}" aria-label="Next"><span aria-hidden="true">»</span></a></li>'.format(self.base_url, self.current_page_num + 1) tab_html += next_page # 尾页 self.params['page'] = self.page_number_count weiye = '<li><a href="{0}?{1}" aria-label="Previous" ><span aria-hidden="true">尾页</span></a></li>'.format( self.base_url, self.params.urlencode()) tab_html += weiye tab_html += '</ul></nav>' return tab_html #函数low鸡版 def pagenation(base_url,current_page_num,total_counts,per_page_counts=10,page_number=5): ''' total_counts数据总数 per_page_counts每页分多少条数据 page_number = 页码显示多少个 current_page_num 当前页 :return: ''' # all_objs_list = models.Customer.objects.all() # total_counts = all_objs_list.count() # page_number = 5 try: current_page_num = int(current_page_num) except Exception: current_page_num = 1 half_page_range = page_number//2 #计算总页数 page_number_count,a = divmod(total_counts,per_page_counts) if current_page_num < 1: current_page_num = 1 if a: page_number_count += 1 if current_page_num > page_number_count: current_page_num = page_number_count start_num = (current_page_num - 1) * 10 end_num = current_page_num * 10 if page_number_count <= page_number: page_start = 1 page_end = page_number_count else: if current_page_num <= half_page_range: page_start = 1 page_end = page_number elif current_page_num + half_page_range >= page_number_count: page_start = page_number_count - page_number + 1 page_end = page_number_count else: page_start = current_page_num - half_page_range page_end = current_page_num + half_page_range #拼接HTMl标签 tab_html = '' tab_html += '<nav aria-label="Page navigation"><ul class="pagination">' #上一页 if current_page_num == 1: previous_page = '<li disabled><a href="#" aria-label="Previous" ><span aria-hidden="true">«</span></a></li>' else: previous_page = '<li><a href="{0}?page={1}" aria-label="Previous" ><span aria-hidden="true">«</span></a></li>'.format(base_url,current_page_num-1) tab_html += previous_page for i in range(page_start,page_end+1): if current_page_num == i: one_tag = '<li class="active"><a href="{0}?page={1}">{1}</a></li>'.format(base_url,i) else: one_tag = '<li><a href="{0}?page={1}">{1}</a></li>'.format(base_url, i) tab_html += one_tag #下一页 if current_page_num == page_number_count: next_page = '<li disabled><a href="#" aria-label="Next"><span aria-hidden="true">»</span></a></li>' else: next_page = '<li><a href="{0}?page={1}" aria-label="Next"><span aria-hidden="true">»</span></a></li>'.format(base_url,current_page_num+1) tab_html+=next_page tab_html += '</ul></nav>' return tab_html,start_num,end_num
视图函数中的调用
视图函数中拿到对应的数据,然后根据上面的类实例化一下就好了~~
但是因为上面提到了“保存搜索条件”~~这里给出完整的get方法的视图函数:
#公户信息与私户信息用同一个视图函数实现! class CustomerView(View): # 判断是公户还是私户的 flag = None # get方法实现了展示所有公户与搜索功能 # 搜索实现了保存搜索条件的分页逻辑--视图函数与page.py中~ def get(self,request): condition = request.GET.get('condition',default='') wd = request.GET.get('wd',default='') # print(condition,wd) # 模糊匹配的,因此这里将条件字段拼接成模糊匹配的形式 condition = condition+'__contains' # get:condition=qq&wd=1&page=2 # <QueryDict: {'condition': ['qq'], 'wd': ['1'], 'page': ['2']}> #condition=qq&wd=1&page=2 # wd有值 说明进行了搜索操作! if wd: # 实例化一个Q查询类的对象 q = Q() # 指定多条件连接符号,默认是and的关系,如果是一个查询条件的话就不写了 q.connector = 'or' # 往q中添加条件~可以添加多个,默认是and的关系 # 注意~以元组的形式添加进去! q.children.append((condition, wd)) # 弄成这种形式:condition=qq&wd=1&page=2 # q.children.append(('qq_name__contains', '小')) # 还可以额外加一些国定的条件 # all_customers = models.Customer.objects.filter(name__contains=wd) # 这样可以用q来筛选了~ # 如果是公户的话,全部筛选 if request.path == reverse('customer:customers'): all_customers = models.Customer.objects.filter(q) # 私户的话 得选择当前销售的私户了 else: q.connector = 'and' q.children.append((condition, wd)) q.children.append(('consultant', request.user)) all_customers = models.Customer.objects.filter(q) # else表示没有查询,返回所有的公户或私户的数据 else: # 查询客户表中销售字段为空的~~就是公户或者私户的所有的数据 # 根据路径判断访问的是公户的还是私户的 if request.path == reverse('customer:customers'): # 表示公户 self.flag = 0 # 公户 表示没有销售 all_customers = models.Customer.objects.filter(consultant__isnull=True) else: # 表示是私户 self.flag = 1 # 这时所有的信息应当是当前登陆用户的客户了 all_customers = models.Customer.objects.filter(consultant=request.user) # 如果有数据就进行处理 if all_customers: current_page_num = request.GET.get('page', default=1) # 每页显示5条 per_page_counts = 5 # 总共显示11个页码 page_number = 11 total_count = all_customers.count() page_obj = page.PageNation(request.path, current_page_num, total_count, request, per_page_counts, page_number) all_customers = all_customers.order_by('-pk')[page_obj.start_num:page_obj.end_num] ret_html = page_obj.page_html() return render(request, 'customers.html', {'all_customers': all_customers, 'ret_html': ret_html,'flag':self.flag}) # 如果没查到数据的话,就返回一个空表格页面 else: return render(request,'customers.html')
上面的代码是:公户与私户在同一个视图函数下的信息展示与搜索~~可以复习一下具体的逻辑。
主要还是看加粗的那部分,这是实现分页的视图函数的核心代码~~
另外,这里做了一个bug修复:如果搜不到数据的话,我给他返回一个空表格页面~
前端的渲染
前端的渲染十分简单!只需要在相应的位置加上标签就好了!
{{ ret_html|safe }}
~~