在 Django 中实现分页

2022-11-13  本文已影响0人  Svon_Book

借助 Django 自带的 Paginator工具,实现类似B站的分页效果

image.png

伪码实现

# views.py
def view_func(request):
    接收 page 页参数
    result = Paginate(待分页对象, page, 每页元素数量)
    return render(...,{'result': result})

class Pagination():
    验证 page 是否合理
    if 空页面 or 非法参数:
        raise Http404

    def items(self):
    返回分页后的对象

    def html_string(self):
    返回 html 文本,便于前端渲染
<!--index.html-->
{% for r in result.items %}
    输入分页元素
{% endfor %}
{% result.html_string %}

1. page 参数验证

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

class Paginate(object):
    def __init__(self, queryset, page, per_page=10):
        """
        :param queryset: 需要分页的对象集合
        :param page: 获取指定页数的对象集合,例如2/20
        :param per_page: 每页对象元素数量,默认10
        """
        self.django_paginator_obj = Paginator(queryset, per_page)
        try:
            self.current_page = self.django_paginator_obj.validate_number(page)
        except EmptyPage:
            raise Http404('该页无内容')
        except PageNotAnInteger:
            raise Http404('页码格式无效')
        self.current_page_items = self.django_paginator_obj.get_page(self.current_page)

2. 返回分页后的对象

class Paginate(object):
    ...
    def items(self):
        """
        按给定的 page 返回分页后的对象

        :return: QuerySet
        """
        return self.current_page_items.object_list

3. 返回HTML 文本标记

3.1 返回后的 HTML 标记格式如下

借助 Bootstrap 样式实现:


image.png

3.2 三种页数情况的处理方法

image.png

3.3 关键代码

class Paginate(object):
    ...
    def page_html_string(self):
        """
        :return: html element
        """
        start = end = 0
        # 处理总页不过5的情况
        if self.django_paginator_obj.num_pages <= 5:
            start = 1
            end = self.django_paginator_obj.num_pages
        else:
            # 处理 “初始页” 极值的情况
            if self.current_page - 2 <= 1:
                start = 1
                end = 5
            # 处理 “最后页” 极值的情况
            elif self.current_page + 2 >= self.django_paginator_obj.num_pages:
                start = self.django_paginator_obj.num_pages - 5 + 1
                end = self.django_paginator_obj.num_pages
            else:
            # 处理常规情况下5个分页连接的的起、止页码
                start = self.current_page - 2
                end = self.current_page + 2
        page_string = "<ul class=\"pagination\">"
        # 处理上一页
        if self.current_page_items.has_previous():
            page_string += "<li><a href=\"?page={}\"><span>&laquo;</span></a></li>".format(
                self.current_page - 1)
        else:
            page_string += "<li class=\"disabled\"><a href=\"#\"><span>&laquo;</span></a></li>"
        # “首页”页码与 “...” 字样
        if self.current_page - 2 > 1:
            page_string += "<li class=\"active\"><a href=\"?page=1\">1</a></li>"
            page_string += "<li><a>...</a></li>"
        # 中间区域5个页码
        for i in range(start, end + 1):
            if i == self.current_page:
                page_string += "<li class=\"active\"><a href=\"?page={0}\">{0}</a></li>".format(i)
            else:
                page_string += "<li><a href=\"?page={0}\">{0}</a></li>".format(i)
        # “尾页”页码与 “...” 字样
        if self.current_page + 2 < self.django_paginator_obj.num_pages:
            page_string += "<li><a>...</a></li>"
            page_string += "<li><a href=\"?page={0}\">{0}</a></li>".format(self.django_paginator_obj.num_pages)
        if self.current_page_items.has_next():
            page_string += "<li><a href=\"?page={}\"><span>&raquo;</span></a></li>".format(
                self.current_page + 1)
        else:
            page_string += "<li class=\"disabled\"><a href=\"#\"><span>&raquo;</span></a></li>"
        page_string += "</ul>"
        return mark_safe(page_string)

最终效果

image.png
上一篇下一篇

猜你喜欢

热点阅读