zoukankan      html  css  js  c++  java
  • 分页组件

    Django的分页器(paginator)简介

    django自带分页组件,但是自带的不是很全,我们需要扩展它内置的原生分页。

    django里面是封装了两个类,通过这两个类去创造了分页器:

    我们在视图函数里面通过django的model操作,获取了数据,然后嵌套到前端HTML文档里去渲染展示,如果数据量多大的话很显示的特别长,这时候我们可以做分页。

    需求:

      每页显示10条内容,看第五页的内容。

      索引是从0开始的,所以第一页的10条内容,索引是0-10。第二页索引是10-20,第三页的索引的20-30.我们发现对所有的内容进行切片就可以了。

      开始的索引是当前页减去1然后乘以10,结束的索引是当前页乘以10。当前页的内容就是所有内容的切片。

    后台实现逻辑传递:
    def
    show(request): #规定每页显示条数 per_page_count = 10 #当前页数 current_page = request.GET.get('page_num') current_page = int(current_page) #索引开始位置 start = (current_page-1) * per_page_count #索引结束位置 stop = current_page * per_page_count # 从数据库获取内容 data_list = model...... # 当前页的内容 current_page_data = data_list[start:stop] # 向前端嵌套需要展示的内容 return render(request,'html文档',{'data_list':current_page_data})
    前端接收数据用模板语言。

    前端的上一页、下一页怎么做?

      a标签跳转

    def show(request):
        # 如果当前页就是第一页没有上一页
        if current_page <=1:
            prev_page = 1
        # 上一页
        prev_page = current_page-1
        # 判断当前页是否是最后一页
        # 下一页
        next_page = current_page+1
        # 向前端嵌套需要展示的内容
        return render(request,'html文档',{'data_list':current_page_data,'prev_page':prev_page,'next_page':next_page})
    <ul>
        <!--接收后台传过来的内容-->
        {% for data in data_list %}
            <li>{{ 数据库的具体记录 }}</li>
        {% endfor %}
    </ul>
    <!--接收后台传过来的内容,上一页,下一页-->
    <a href="请求的url?p={{ prev_page }}">上一页</a>
    <a href="请求的url?p={{ next_page }}">上一页</a>

    以上就是分页的原理。

    def show(request):
        #规定每页显示条数
        per_page_count = 10
        #当前页数
        current_page = request.GET.get('page_num')
        current_page = int(current_page)
        #索引开始位置
        start = (current_page-1) * per_page_count
        #索引结束位置
        stop = current_page * per_page_count
        # 从数据库获取内容
        data_list = model......
        # 当前页的内容
        current_page_data = data_list[start:stop]
        # 如果当前页就是第一页没有上一页
        if current_page <=1:
            prev_page = 1
        # 上一页
        prev_page = current_page-1
        # 判断当前页是否是最后一页
        # 下一页
        next_page = current_page+1
        # 向前端嵌套需要展示的内容
        return render(request,'html文档',{'data_list':current_page_data,'prev_page':prev_page,'next_page':next_page})
    Views
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Title</title>
    </head>
    <body>
    <ul>
        <!--接收后台传过来的内容-->
        {% for data in data_list %}
            <li>{{ 数据库的具体记录 }}</li>
        {% endfor %}
    </ul>
    <!--接收后台传过来的内容,上一页,下一页-->
    <a href="请求的url?p={{ prev_page }}">上一页</a>
    <a href="请求的url?p={{ next_page }}">上一页</a>
    </body>
    </html>
    html

    以上我们写的代码django原生的就能满足我们的需求。所以我们可以不用自己写这个代码。

    使用django内置的分页:

      第一步:需要引入分页组件,django分页的所有东西都在里面了

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

    我们发现Paginator就是一个类。我们使用这个类时就可以

    class Paginator(object):
    
        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
    
        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)
    
        def _check_object_list_is_ordered(self):
            """
            Warn if self.object_list is unordered (typically a QuerySet).
            """
            ordered = getattr(self.object_list, 'ordered', None)
            if ordered is not None and not ordered:
                obj_list_repr = (
                    '{} {}'.format(self.object_list.model, self.object_list.__class__.__name__)
                    if hasattr(self.object_list, 'model')
                    else '{!r}'.format(self.object_list)
                )
                warnings.warn(
                    'Pagination may yield inconsistent results with an unordered '
                    'object_list: {}.'.format(obj_list_repr),
                    UnorderedObjectListWarning,
                    stacklevel=3
                )
    
    
    QuerySetPaginator = Paginator   # For backwards-compatibility.
    Paginator

    我们使用这个类时就可以给它传值。

      第二步:创建Paginator对象,Paginator的参数有object_list, per_page, orphans=0,allow_empty_first_page=True

    • object_list 是要分页的对象,如果是django的话可以直接写model的操作,比如:
    • per_page  每页显示条目数量
    from django.shortcuts import render
    from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
    def show(request):
        paginator = Paginator(book_list, 10)

      paginator里面封装了几个常用的方法:

    • paginator.per_page             查看每页显示条目数量
    • paginator.count                  查看总个数
    • paginator.num_pages          查看总页数
    • paginator.range                  总页数的索引范围
    • paginator.page                   page对象
    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
    Page
    def show(request):
        current_page = request.GET.get('page-num')
        paginator = Paginator(models.Book.obiects.all(),10)
        # 异常处理,以防客户输入的不正确
        try:
            posts = paginator.page(current_page)
        #填的不是个整数
        except PageNotAnInteger:
            posts = paginator.page(1)
        # 空页
        except EmptyPage:
            posts = paginator.page(paginator.num_pages)
        return render(request,'book.html',{'posts':posts})

    page对象里面封装了结果方法:

    • posts.has_next                    是否有下一页
    • posts.next_page_number            下一页页码
    • posts.has_previous                                   是否有上一页
    • posts.previous_page_number                     上一页页码
    • posts.object_list                                      分页之后的数据列表,已经切片好的数据
    • posts.number                                          当前页
    • posts.paginator                                       paginator对象
    <!-- 数据内容 -->
    <ul>
        {% for book in posts.object_list %}
            <li>{{ book.id }} - {{ book.name }}</li>
        {% endfor %}
    </ul>
    <!-- 上一页和下一页 -->
    {% if posts.has_prvious %}
        <a href="url?={{ posts.prvious_page_number }}">上一页</a>
    {% else %}
        <a href="#">上一页</a>
    {% endif %}
    
    {% if posts.has_next %}
        <a href="url?={{ posts.next_page_number }}">下一页</a>
    {% else %}
        <a href="#">下一页</a>
    {% endif %}

    显示总页数和当前页。

    <span>
    {{ posts.number }}/{{ posts.paginator.num_pages }}
    </span>

    和bootstrap配合使用:

    <nav aria-label="...">
        <ul class="pager">
            {% if posts.has_previous %}
                 <li><a href="/show?p={{ posts.previous_page_number }}">上一页</a></li>
            {% else %}
                <li><a href="#">上一页</a></li>
            {% endif %}
    
            {% if posts.has_next %}
                <li><a href="/show?p={{ posts.next_page_number }}">下一页</a></li>
            {% else %}
                <li><a href="#">下一页</a></li>
            {% endif %}
        </ul>
    </nav>
    
    <span>
        {{ posts.number }}/{{ posts.paginator.num_pages }}
    </span>
    View Code

    每一个都会有上一页下一页,都会分页。我们可以新建个include文件夹,将写的这些模板语言放到pager.html文件里面。如果别的HTML文档需要用可以{% include  'include/pager.html'%}就可以了。

    用django的分页器也就只能做到这里了。 

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    from app01.models import *
    from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
    
    def index(request):
    
        '''
        批量导入数据:
    
        Booklist=[]
        for i in range(100):
            Booklist.append(Book(title="book"+str(i),price=30+i*i))
        Book.objects.bulk_create(Booklist)
        '''
    
        '''
    分页器的使用:
    
        book_list=Book.objects.all()
    
        paginator = Paginator(book_list, 10)
    
        print("count:",paginator.count)           #数据总数
        print("num_pages",paginator.num_pages)    #总页数
        print("page_range",paginator.page_range)  #页码的列表
    
    
    
        page1=paginator.page(1) #第1页的page对象
        for i in page1:         #遍历第1页的所有数据对象
            print(i)
    
        print(page1.object_list) #第1页的所有数据
    
    
        page2=paginator.page(2)
    
        print(page2.has_next())            #是否有下一页
        print(page2.next_page_number())    #下一页的页码
        print(page2.has_previous())        #是否有上一页
        print(page2.previous_page_number()) #上一页的页码
    
    
    
        # 抛错
        #page=paginator.page(12)   # error:EmptyPage
    
        #page=paginator.page("z")   # error:PageNotAnInteger
    
        '''
    
    
        book_list=Book.objects.all()
    
        paginator = Paginator(book_list, 10)
        page = request.GET.get('page',1)
        currentPage=int(page)
    
    
        try:
            print(page)
            book_list = paginator.page(page)
        except PageNotAnInteger:
            book_list = paginator.page(1)
        except EmptyPage:
            book_list = paginator.page(paginator.num_pages)
    
    
        return render(request,"index.html",{"book_list":book_list,"paginator":paginator,"currentPage":currentPage})
    views
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" 
        integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    </head>
    <body>
    
    <div class="container">
    
        <h4>分页器</h4>
        <ul>
    
            {% for book in book_list %}
                 <li>{{ book.title }} -----{{ book.price }}</li>
            {% endfor %}
    
         </ul>
    
    
        <ul class="pagination" id="pager">
    
                     {% if book_list.has_previous %}
                        <li class="previous"><a href="/index/?page={{ book_list.previous_page_number }}">上一页</a></li>
                     {% else %}
                        <li class="previous disabled"><a href="#">上一页</a></li>
                     {% endif %}
    
    
                     {% for num in paginator.page_range %}
    
                         {% if num == currentPage %}
                           <li class="item active"><a href="/index/?page={{ num }}">{{ num }}</a></li>
                         {% else %}
                           <li class="item"><a href="/index/?page={{ num }}">{{ num }}</a></li>
    
                         {% endif %}
                     {% endfor %}
    
    
    
                     {% if book_list.has_next %}
                        <li class="next"><a href="/index/?page={{ book_list.next_page_number }}">下一页</a></li>
                     {% else %}
                        <li class="next disabled"><a href="#">下一页</a></li>
                     {% endif %}
    
                </ul>
    </div>
    
    
    
    </body>
    </html>
    html

    扩展

    需求:

      我们想要上一页和下一页中间显示数值,但不是全部显示出来,就显示几个就可以了。

    怎么做?

      目前django自带的分页器不带这个功能,所以需要我们自己开发出来这个功能。django的分页器只涉及Paginator对象和Page对象,如果再这两个对象里随便一个,return一个范围,能在页面让它这样输出。比如在Page对象return 一到六,那那个页面就显示1到6。

      但是上面的Page对象是Paginator类里面的方法创建的,我们自己想要定义一个类需要修改源码,但是源码不能随便改。

      但是Paginator对象使我们自己创建的。

        我们可以自己创建一个类继承Paginator,并且为了不影响其他的功能我们可以super。

        这个类可以传参数,可以自己定义方法。

       显示左5,右5,总共11个页,
    如果总页码大于11
            1.1 if 当前页码减5小于1,要生成1到12的列表(顾头不顾尾,共11个页码)
                page_range=range(1,12)
            1.2 elif 当前页码+5大于总页码,生成当前页码减10,到当前页码加1的列表(顾头不顾尾,共11个页码)
                page_range=range(paginator.num_pages-10,paginator.num_pages+1)
            1.3 else 生成当前页码-5,到当前页码+6的列表
                page_range=range(current_page_num-5,current_page_num+6)
    其它情况,生成的列表就是pageinator的page_range
            page_range=paginator.page_range
    
        '''
    核心逻辑
    from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
    
    class CustomPaginator(Paginator):
        def __init__(self, *args, **kwargs):
            super(CustomPaginator, self).__init__(*args, **kwargs)
    
        def pager_num_range(self):
            # 前端怎么获取?前端现在有page对象,page对象.paginator就是上层的paginator对象,我们这里改了paginator对象现在变成CustomPaginator对象了。我们多增加了pager_num_range参数。
            return range(1, 12)
    def show(request):
        current_page = request.GET.get('p')
        book_list = models.Book.objects.all()
        paginator = CustomPaginator(book_list, 10)
        # 异常处理,以防客户输入的不正确
        try:
            posts = paginator.page(current_page)
        # 填的不是个整数
        except PageNotAnInteger:
            posts = paginator.page(1)
        # 空页
        except EmptyPage:
            posts = paginator.page(paginator.num_pages)
        return render(request,'show.html', {'posts': posts})

    在前端怎么获取?

      在上一页和下一页的中间来个for循环,并且给当前页的页码来个样式

    {% for i in posts.paginator.pager_num_range %}
        {% if i == posts %}
            <a style="font-size: 20px" href="/show?p={{ i }}">{{ i }}</a>
            {% else %}
            <a href="/show?p={{ i }}">{{ i }}</a>
        {% endif %} 

    问题:

    range应该是动态生成的,不能写死了。

      根据当前页动态生成!

    根据当前页、最多显示多少页和数据的总页数。

      如果总页数小于最大显示的页码,我们就之显示总页数的页码。

      总页数特别多

        如果当前页是1-6之间的时候

    from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
    from app01 import models
    class CustomPaginator(Paginator):
        def __init__(self,current_page,per_pager_num,*args, **kwargs):
            # 当前页
            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):
            # 前端怎么获取?前端现在有page对象,page对象.paginator就是上层的paginator对象,我们这里改了paginator对象现在变成CustomPaginator对象了。我们多增加了pager_num_range参数。
            if self.num_pages < self.per_pager_num:
                return range(1,self.num_pages+1)
            part = int(self.per_pager_num/2)
            if self.current_page <= part:
                return range(1,self.per_pager_num+1)
            if (self.current_page+5) > self.num_pages:
                return range(self.num_pages-self.per_pager_num-1,self.num_pages+1)
            return range(self.current_page-part, self.current_page+part+1)
    def show(request):
        current_page = request.GET.get('p')
        book_list = models.Book.objects.all()
        paginator = CustomPaginator(current_page,11,book_list, 10)
        # 异常处理,以防客户输入的不正确
        try:
            posts = paginator.page(current_page)
        # 填的不是个整数
        except PageNotAnInteger:
            posts = paginator.page(1)
        # 空页
        except EmptyPage:
            posts = paginator.page(paginator.num_pages)
        return render(request,'show.html', {'posts': posts})

     

    自定义分页组件

    自定义需要传入:

    • 所有数据的个数
      • 比如有100万条数据,如果全拿出来服务器就炸了,所以应该去数据库查一共有多少条数据,然后通过计算这一次只取多少到多少,把索引告诉数据库,让数据库把这一块位置拿出来就好了。。  
    • 当前页
    • 每页显示30条
    • 最多显示7页    

    效果: 

    自定义的分页类:

    在逻辑函数里用:

    第一步是导入这个类 

    第二步是创建这个类的对象,并且传入参数

      第一个参数是所有数据的个数[在数据库中获取所有数据的个数方法是:total_count = models.数据库表名.objects.all().count()] 

      第二个参数是当前页[在request请求里获取:current_page = request.GET.get('p')] 

      第三个参数是每页显示的行数

       

    我们自定义的分页py文件内容:

    class Pagination(object):
    
        def __init__(self, totalCount, currentPage, perPageItemNum=30, maxPageNum=7):
            # 数据总个数
            self.total_count = totalCount
            # 当前页,客户有可能写错
            try:
                v = int(currentPage)
                if v <= 0:
                    v = 1
                else:
                    self.current_page = v
            except Exception as e:
                self.current_page = 1
            # 每页显示的行数
            self.per_page_item_num = perPageItemNum
            # 最多显示的页码
            self.max_page_num = maxPageNum
    
        # 数据切片
        def start(self):
            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):
            a, b = divmod(self.total_count, self.per_page_item_num)
            if b == 0:
                return a
            else:
                return a + 1
    
        # 分页的范围
        def pager_num_range(self):
            # 前端怎么获取?前端现在有page对象,page对象.paginator就是上层的paginator对象,我们这里改了paginator对象现在变成CustomPaginator对象了。我们多增加了pager_num_range参数。
            if self.num_pages < self.max_page_num:
                return range(1, self.num_pages + 1)
            part = int(self.max_page_num / 2)
            if self.current_page <= part:
                return range(1, self.max_page_num + 1)
            if (self.current_page + 5) > self.num_pages:
                return range(self.num_pages - self.max_page_num - 1, self.num_pages + 1)
            return range(self.current_page - part, self.current_page + part + 1)
    
        # 自动生成a标签
        def page_str(self):
            page_list = []
            first = "<li><a href='/show?p=1'>首页</a></li>"
    
            page_list.append(first)
            if self.current_page == 1:
                prev = "<li><a href='#'>上一页</a></li>"
            else:
                prev = "<li><a href='/show?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='/book?p=%s'>%s</a></li>" % (i, i)
                else:
                    temp = "<li><a href='/show?p=%s'>%s</a></li>" % (i, i)
                page_list.append(temp)
    
            if self.current_page == self.num_pages:
                next = "<li><a href='#'>下一页</a></li>"
            else:
                next = "<li><a href='/show?p=%s'>下一页</a></li>" % (self.current_page + 1)
            page_list.append(next)
    
            last = "<li><a href='/show?p=%s'>尾页</a></li>" % (self.num_pages)
            page_list.append(last)
    
            return ''.join(page_list)
    pager.py

    如何使用:

      在视图逻辑中:

    def show(request):
        from app01.pager import Pagination
        current_page = request.GET.get('p')
        total_count = models.Book.objects.all().count()
        pager_obj = Pagination(total_count,current_page)
        book_list = models.Book.objects.all()
        data_list = book_list[pager_obj.start():pager_obj.end()]
        return render(request,'book.html',{'data':data_list,'page_obj':pager_obj})

      在HTML文件中:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
        <title>Title</title>
    </head>
    <body>
    <h4>分页器</h4>
    <ul>
        {% for book in data %}
            <li>{{ book.title }} -----{{ book.price }}</li>
        {% endfor %}
    </ul>
    
    <ul class="pagination pagination-sm">
        {{ page_obj.page_str|safe }}
    </ul>
    </body>
    </html>

    end

  • 相关阅读:
    关于button去掉自带阴影效果的方法
    关于含RecyclerView的fragment来回切换时页面自动滑动到底部的解决方法
    关于简单的安卓APP状态栏设置(类似沉浸式状态栏)
    关于TabLayout与ViewPager在Fragment中嵌套Fragment使用或配合使用的思考
    关于安卓一些报错的解决方法(随时更新)
    react与微信小程序
    微信小程序js学习心得体会
    网络流学习笔记
    git 错误 RPC
    LeetCode 翻转链表
  • 原文地址:https://www.cnblogs.com/zhangrenguo/p/12754345.html
Copyright © 2011-2022 走看看