YOLO813

Django项目翻页功能的修改 - 数据来源于Elasticsearch

    之前,在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 %}