之前,在Django视图补充-快速建立翻页中记录过利用ListView建立分类页,也一直是这么做的,但是由于我的数据是从elasticsearch中取出,并且有一些特殊的需求,建立一个翻页功能简直要了我的老命,最近研究了下Paginator和Page,勉强可以使用了。
from django.core.paginator import Paginator
from django.core.paginator import Page
两大类的源码就不解释了,毕竟我自己也是一知半解。在我理解中,Paginator主要是控制数据的range范围,总页数,总条数等,Page则用于判断当前页数的一些属性,例如:是否存在上、下一页,上、下一页的具体数字。
先直接贴上源代码,再来解释:
# 完成了自定义列表页
class CategoryView(View):
template_name= "name.html"
page_kwarg = 'p'
paginate_by = 10 # 默认展示条数
max_display_page = 50 # 默认展示页数
@classmethod
def get(cls, req, name):
current_page = req.GET.get(cls.page_kwarg)
# 过滤用户条件
try:
current_page = int(current_page)
except:
current_page = 1
if current_page >= cls.max_display_page:
current_page = cls.max_display_page
if current_page<= 0:
current_page = 1
my_search,list,counts = es.getCategory(name, display_number=cls.paginate_by,my_page=current_page)
context = {
"list": list,
"counts":counts
}
paginator = Paginator(my_search, cls.paginate_by)
page_obj = Page(my_search,current_page,paginator)
res = cls.getPaginationInfo(paginator, page_obj)
context.update(res)
return render(req, template_name=cls.template_name, context=context)
@classmethod
def getPaginationInfo(cls, paginator, page_obj):
current_page = page_obj.number
try: #小于1会报错
cus_previous_page_number = page_obj.previous_page_number()
except:
cus_previous_page_number = 1
try:
cus_next_page_number = page_obj.next_page_number()
except: # 防止报错,瞎定义的
cus_next_page_number = 10
left_has_more = False
right_has_more = False
if current_page > 1:
left_has_more = True
if current_page < paginator.num_pages:
right_has_more = True
return {
'left_has_more': left_has_more,
'right_has_more': right_has_more,
'num_pages': paginator.num_pages,
'cus_previous_page_number':cus_previous_page_number,
'cus_next_page_number':cus_next_page_number
}
这个views分为两部分,一部分是当用户访问这个页面时,我通过req.GET.get(cls.page_kwarg)函数获取用户访问的页码,为什么使用get函数而不直接采用字典取值['p']这种形式呢?因为你永远不知道用户的输入会是什么,我们得做最坏的打算。尝试将页码转化为int类型,如果非数值,则默认当前页为第一页;同时对于用户翻页的最大值max_display_page作出了限制。将页数和每页展示的数量传入到es搜索中,以获得不同偏移量的数据;再将获取到的结果传入到上下文context,方便前端模板取值。再按照Paginator和Page的规则定义,分别传入object_list、per_page和object_list, number, 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
class Page(collections.abc.Sequence):
def __init__(self, object_list, number, paginator):
self.object_list = object_list
self.number = number
self.paginator = paginator
定义函数getPaginationInfo用于将是否存在上、下一页,以及页码信息传入到上下文中,返回给前端模板页。下面我接了一个抛出异常的操作,原因是查看Page的previous_page_number函数时,可以发现该函数调用的是paginator的validate_number函数(参看下方代码),而该函数在传入的number<1时会抛出一个EmptyPage异常,所以我在外面包裹了一层,当然还存在其它做法
def validate_number(self, number):
"""Validate the given 1-based page number."""
try:
if isinstance(number, float) and not number.is_integer():
raise ValueError
number = int(number)
except (TypeError, ValueError):
raise PageNotAnInteger(_('That page number is not an integer'))
if number < 1:
raise EmptyPage(_('That page number is less than 1'))
if number > self.num_pages:
if number == 1 and self.allow_empty_first_page:
pass
else:
raise EmptyPage(_('That page contains no results'))
return number
将该函数更新到上下文context中,前端模板就可以愉快的添加翻页了。
{% if left_has_more %}
<a href="{% url 'category' name=request.path|getLastPath %}?p={{cus_previous_page_number}}">Previous</a>
{% endif %}
{% if right_has_more %}
<a href="{% url 'category' name=request.path|getLastPath %}?p={{cus_next_page_number}}">Next</a>
{% endif %}