• django之分页


    分页

    在当前网页如果特别的数据,如果不想当前网页显示太多的内容数据(例如一些文章的目录或者一些小说的章节目录很多地方都用到分页),这时候就需要用分页了。

    在Django中数据被分在不同页面中,并带有“上一页/下一页”标签。这些类位于django/core/paginator.py中。

    分页器Paginator

    Paginator.py源码

    import collections
    from math import ceil
    
    from django.utils import six
    from django.utils.functional import cached_property
    
    
    class InvalidPage(Exception):
        pass
    
    
    class PageNotAnInteger(InvalidPage):
        pass
    
    
    class EmptyPage(InvalidPage):
        pass
    
    
    class Paginator(object):
    
        def __init__(self, object_list, per_page, orphans=0,
                     allow_empty_first_page=True):
            self.object_list = object_list
            self.per_page = int(per_page)
            self.orphans = int(orphans)
            self.allow_empty_first_page = allow_empty_first_page
    
        def validate_number(self, number):
            """
            Validates the given 1-based page number.
            """
            try:
                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
    
        def page(self, number):
            """
            Returns a Page object for the given 1-based page number.
            """
            number = self.validate_number(number)
            bottom = (number - 1) * self.per_page
            top = bottom + self.per_page
            if top + self.orphans >= self.count:
                top = self.count
            return self._get_page(self.object_list[bottom:top], number, self)
    
        def _get_page(self, *args, **kwargs):
            """
            Returns an instance of a single page.
    
            This hook can be used by subclasses to use an alternative to the
            standard :cls:`Page` object.
            """
            return Page(*args, **kwargs)
    
        @cached_property
        def count(self):
            """
            Returns the total number of objects, across all pages.
            """
            try:
                return self.object_list.count()
            except (AttributeError, TypeError):
                # AttributeError if object_list has no count() method.
                # TypeError if object_list.count() requires arguments
                # (i.e. is of type list).
                return len(self.object_list)
    
        @cached_property
        def num_pages(self):
            """
            Returns the total number of pages.
            """
            if self.count == 0 and not self.allow_empty_first_page:
                return 0
            hits = max(1, self.count - self.orphans)
            return int(ceil(hits / float(self.per_page)))
    
        @property
        def page_range(self):
            """
            Returns a 1-based range of pages for iterating through within
            a template for loop.
            """
            return six.moves.range(1, self.num_pages + 1)
    
    
    QuerySetPaginator = Paginator   # For backwards-compatibility.
    
    
    class Page(collections.Sequence):
    
        def __init__(self, object_list, number, paginator):
            self.object_list = object_list
            self.number = number
            self.paginator = paginator
    
        def __repr__(self):
            return '<Page %s of %s>' % (self.number, self.paginator.num_pages)
    
        def __len__(self):
            return len(self.object_list)
    
        def __getitem__(self, index):
            if not isinstance(index, (slice,) + six.integer_types):
                raise TypeError
            # The object_list is converted to a list so that if it was a QuerySet
            # it won't be a database hit per __getitem__.
            if not isinstance(self.object_list, list):
                self.object_list = list(self.object_list)
            return self.object_list[index]
    
        def has_next(self):
            return self.number < self.paginator.num_pages
    
        def has_previous(self):
            return self.number > 1
    
        def has_other_pages(self):
            return self.has_previous() or self.has_next()
    
        def next_page_number(self):
            return self.paginator.validate_number(self.number + 1)
    
        def previous_page_number(self):
            return self.paginator.validate_number(self.number - 1)
    
        def start_index(self):
            """
            Returns the 1-based index of the first object on this page,
            relative to total objects in the paginator.
            """
            # Special case, return zero if no items.
            if self.paginator.count == 0:
                return 0
            return (self.paginator.per_page * (self.number - 1)) + 1
    
        def end_index(self):
            """
            Returns the 1-based index of the last object on this page,
            relative to total objects found (hits).
            """
            # Special case for the last page because there can be orphans.
            if self.number == self.paginator.num_pages:
                return self.paginator.count
            return self.number * self.paginator.per_page
    

      

    使用分页器Paginator

    在视图中使用 Paginator来为查询集分页。我们提供视图以及相关的模板来展示如何展示这些结果。

    Paginator常用属性

    per_page: 每页显示条目数量
    count:    数据总个数
    num_pages:总页数
    page_range:总页数的索引范围,页码的范围,从1开始,例如[1, 2, 3, 4]。
    

      

    Paginator所需参数

    object_list   一个列表,元祖或则Django 的Queryset 对象 或则其他对象带有 count() or __len__()的方法
    
    per_page :就是1页显示几条数据
    

      

    Paginator对象的方法

    page(number)   :返回在提供的下标处的Page对象,下标以1开始。
    

      

    使用page对象

    方法

    Page.has_next()
    如果有下一页,则返回True。
    
    Page.has_previous()
    如果有上一页,返回 True。
    
    Page.has_other_pages()
    如果有上一页或下一页,返回True。
    
    Page.next_page_number()
    返回下一页的页码。如果下一页不存在,抛出InvalidPage异常。
    
    Page.previous_page_number()
    返回上一页的页码。如果上一页不存在,抛出InvalidPage异常。
    
    Page.start_index()
    返回当前页上的第一个对象,相对于分页列表的所有对象的序号,从1开始。比如,将五个对象的列表分为每页两个对象,第二页的start_index()会返回3。
    
    Page.end_index()
    返回当前页上的最后一个对象,相对于分页列表的所有对象的序号,从1开始。 比如,将五个对象的列表分为每页两个对象,第二页的end_index() 会返回 4。
    

      

    属性

    Page.object_list
    当前页上所有对象的列表。
    
    Page.number
    当前页的序号,从1开始。
    
    Page.paginator
    相关的Paginator对象。
    

      

    django内置分页

    代码示例

    views.py

      

    from django.shortcuts import render
    from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger
    # Create your views here.
    
    #模拟测试网页数据
    USER_LIST = []
    for i in range(1,999):
        temp = {"name":"root"+str(i),"age":i}
        USER_LIST.append(temp)
    
    def listing(request):
        current_page = request.GET.get('p')
        paginator = Paginator(USER_LIST,10)
        try:
            paginator = paginator.page(current_page)  #获取前端传过来显示当前页的数据
        except PageNotAnInteger:
            # 如果有异常则显示第一页
            paginator = paginator.page(1)
        except EmptyPage:
            # 如果没有得到具体的分页内容的话,则显示最后一页
            paginator = paginator.page(paginator.num_pages)
    
        return render(request,'index.html',{"users":paginator})
    

      

    HTML

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <ul>
        {% for user in users.object_list %}
            <li>{{ user.name }}-{{ user.age }}</li>
        {% endfor %}
        {% if users.has_previous %}
            <a href="/index?p={{ users.previous_page_number }}">上一页</a>
        {% endif %}
        {% if users.has_next %}
            <a href="/index?p={{ users.next_page_number }}">下一页</a>
        {% endif %}
        <span>{{ users.number }}/{{ users.paginator.num_pages }}</span>
    </ul>
    </body>
    </html>
    

      

    模拟访问测试

    http://127.0.0.1:8080/index/?p=123132
    
    http://127.0.0.1:8080/index/?p=阿斯顿发斯蒂芬
    

      

    扩展Django内置分页

     上面差不多久这些功能,为了扩张更多的功能我们来扩展Paginator分页器,例如网页页面显示的页码不只是只显示(上一页,下一页)

    代码示例

    from django.shortcuts import render
    from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger
    # Create your views here.
    
    #模拟测试网页数据
    USER_LIST = []
    for i in range(1,999):
        temp = {"name":"root"+str(i),"age":i}
        USER_LIST.append(temp)
    
    
    class CustomPaginator(Paginator):
        def __init__(self,current_page,per_pager_num,*args,**kwargs):
            # per_pager_num  显示的页码数量
            self.current_page = int(current_page)
            self.per_pager_num = int(per_pager_num)
            super(CustomPaginator,self).__init__(*args,**kwargs)
        def pager_num_range(self):
            '''
            自定义显示页码数
            第一种:总页数小于显示的页码数
            第二种:总页数大于显示页数  根据当前页做判断  a 如果当前页大于显示页一半的时候  ,往右移一下
                                                    b 如果当前页小于显示页的一半的时候,显示当前的页码数量
            第三种:当前页大于总页数
            :return:
            '''
            if self.num_pages < self.per_pager_num:
                return range(1,self.num_pages+1)
    
            half_part = int(self.per_pager_num/2)
            if self.current_page <= half_part:
                return range(1,self.per_pager_num+1)
    
            if (self.current_page+half_part) > self.num_pages:
                return range(self.num_pages-self.per_pager_num+1,self.num_pages)
            return range(self.current_page-half_part,self.current_page+half_part+1)
    
    
    def listing(request):
        current_page = request.GET.get('p')
        paginator = CustomPaginator(current_page,11,USER_LIST,10)
        try:
            paginator = paginator.page(current_page)  #获取前端传过来显示当前页的数据
        except PageNotAnInteger:
            # 如果有异常则显示第一页
            paginator = paginator.page(1)
        except EmptyPage:
            # 如果没有得到具体的分页内容的话,则显示最后一页
            paginator = paginator.page(paginator.num_pages)
    
        return render(request,'index.html',{"users":paginator})
    

      

    HTML

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <ul>
        {% for user in users.object_list %}
            <li>{{ user.name }}-{{ user.age }}</li>
        {% endfor %}
    
        {% if users.has_previous %}
            <a href="/index?p={{ users.previous_page_number }}">上一页</a>
        {% endif %}
    
        {% for number in users.paginator.pager_num_range %}
            {% if number == users.number %}
                <a href="/index?p={{ number }}" style="font-size: 33px">{{ number }}</a>
            {% else %}
                <a href="/index?p={{ number }}" >{{ number }}</a>
            {% endif %}
    
        {% endfor %}
    
    
        {% if users.has_next %}
            <a href="/index?p={{ users.next_page_number }}">下一页</a>
        {% endif %}
        <span>{{ users.number }} /{{ users.paginator.num_pages }}</span>
    </ul>
    </body>
    </html>
    

      

    自定制分页器

    分页功能在每个网站都是必要的,对于分页来说,其实就是根据用户的输入计算出应该在数据库表中的起始位置。

    1、设定每页显示数据条数

    2、用户输入页码(第一页、第二页...)

    3、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置

    4、在数据表中根据起始位置取值,页面上输出数据

    需求,需要在页面上显示分页的页面。如:[上一页][1][2][3][4][5][下一页]

    1、设定每页显示数据条数

    2、用户输入页码(第一页、第二页...)

    3、设定显示多少页号

    4、获取当前数据总条数

    5、根据设定显示多少页号和数据总条数计算出,总页数

    6、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置

    7、在数据表中根据起始位置取值,页面上输出数据

    8、输出分页html,如:[上一页][1][2][3][4][5][下一页]

    代码示例;

    1.新建一个分页器的py文件

    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    from django.utils.safestring import mark_safe
    
    #mark_safe 把字符串转换成html语言
    class Pagination(object):
        def __init__(self,totalCount,currentPage,perPageItemNum=10,maxPageNum=7):
            '''
    
            :param totalCount:   数据总个数
            :param currentPage:   当前页
            :param perPageItemNum:  每页显示行数
            :param maxPageNum:  最多显示页面
            '''
    
            self.total_count = totalCount
    
            try:
                current_page = int(currentPage)
                if current_page <=0:
                    current_page = 1
                self.current_page = current_page
            except Exception as e:
                self.current_page = 1
    
            self.per_page_item_num = perPageItemNum
    
            self.max_page_num = maxPageNum
    
        def  start(self):
            '''
            自定义一个表达式进行显示几页到几页
            :return:
            '''
            return (self.current_page-1)*self.per_page_item_num
    
        def end(self):
            return self.current_page * self.per_page_item_num
    
        @property
        def num_pages(self):
            '''
            num_pages 属性 总页数
            :return:
            '''
            a,b=divmod(self.total_count,self.per_page_item_num)
    
            if b == 0:
                return a
            return a+1
        def pager_num_range(self):
            '''
    
            :return:
            '''
    
            #总页码小于显示的页码区域
            if self.num_pages < self.max_page_num:
                return range(1,self.max_page_num+1)
    
            #总页数大于最多显示页  根据当前页做判断  a,b,c
    
            #a 如果当前页小于显示页一半的时候  ,就只显示当前的页码区域
    
            part = int(self.max_page_num / 2)
            if self.current_page <= part:
                return range(1, self.max_page_num + 1)
    
            #b 如果当前页大于显示页一半的时候  ,往右移一下页码
            if (self.current_page + part) > self.num_pages:
                return range(self.num_pages - self.max_page_num + 1, self.num_pages + 1)
    
            #c:如果当前页大于总页数的
            return range(self.current_page - part, self.current_page + part + 1)
    
        def page_str(self):
    
            '''
    		把自定义后端的一些上一页和下一页的一些标签等等相关内容放到前端去显示
    
            :return:
            '''
    
            page_list = []
            first = "<li><a href='/index2?p=1'>首页</a></li>"
            page_list.append(first)
    
            if self.current_page == 1:
                prev = "<li><a href='#'>上一页</a></li>"
            else:
                prev = "<li><a href='/index2?p=%s'>上一页</a></li>" % (self.current_page - 1,)
            page_list.append(prev)
            for i in self.pager_num_range():
                if i == self.current_page:
                    temp = "<li class='active'><a href='/index2?p=%s'>%s</a></li>" % (i, i)
                else:
                    temp = "<li><a href='/index2?p=%s'>%s</a></li>" % (i, i)
                page_list.append(temp)
    
            if self.current_page == self.num_pages:
                nex = "<li><a href='#'>下一页</a></li>"
            else:
                nex = "<li><a href='/index2?p=%s'>下一页</a></li>" % (self.current_page + 1,)
            page_list.append(nex)
    
            last = "<li><a href='/index2?p=%s'>尾页</a></li>" % (self.num_pages,)
            page_list.append(last)
            res = ''.join(page_list)
    
            return mark_safe(res)
    

      

    2.url路由

    from django.conf.urls import url
    from django.contrib import admin
    from  app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^index/$', views.listing),
        url(r'^index2/$', views.index2),
    ]
    

      

    3.视图

    from django.shortcuts import render
    from app01.pager import Pagination
    
    #模拟测试网页数据
    USER_LIST = []
    for i in range(1,999):
        temp = {"name":"root"+str(i),"age":i}
        USER_LIST.append(temp)
    def index2(request):
        current_page = request.GET.get('p')
        page_obj = Pagination(len(USER_LIST), current_page)
    
        data_list = USER_LIST[page_obj.start():page_obj.end()]
        return render(request, 'index2.html', {'data': data_list, 'page_obj': page_obj})
    

      

    3.模板的html文件index2.html的内容

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <ul>
            {% for row in data %}
                <li>{{ row.name }}-{{ row.age }}</li>
            {% endfor %}
        </ul>
        {% for i in page_obj.pager_num_range %}
            页码显示部分区域
            <a href="/index2?p={{ i }}">{{ i }}</a>
        {% endfor %}
        <hr>
        <ul >{{ page_obj.page_str }}</ul>
    </body>
    </html>
    

      

    4.访问测试

    http://127.0.0.1:9999/index2/?p=4 
    
    注:是用的本机做测试
    

      

     总结:

    总结,分页时需要做三件事:

    • 创建处理分页数据的类
    • 根据分页数据获取数据
    • 输出分页HTML,即:[上一页][1][2][3][4][5][下一页]
  • 相关阅读:
    Visual Studio日志
    选择jQuery的理由
    第三方开发者可将JIT和编译器引入WinRT吗?
    Visual Studio 2012和.NET 4.5已经就绪!
    500TB——Facebook每天收集的数据量
    Netflix开源他们的另一个架构——Eureka
    PhoneGap 2.0 发布
    快速哈希算法破坏了加密的安全性
    Blend for Visual Studio 2012:为Windows 8应用所用的UX工具
    系统中的故障场景建模
  • 原文地址:https://www.cnblogs.com/keme/p/6556871.html
Copyright © 2020-2023  润新知