zoukankan      html  css  js  c++  java
  • Django学习笔记(12)——分页功能

      这一篇博客记录一下自己学习Django中分页功能的笔记。分页功能在每个网站都是必要的,当页面因需要展示的数据条目过多,导致无法全部显示,这时候就需要采用分页的形式进行展示。

      分页在网站随处可见,下面展示一个分页的样式:

       分页的实现,不仅提高了用户的体验,还减轻了数据库读取数据的压力。Django自带名为Paginator的分页工具,方便我们实现分页功能,这个类存放在django/core/paginator.py。它可以接收列表,元组或者其他可迭代对象。

       下面先学习一下Paginator的基本语法。

    Django中Paginator基本语法

    1,分页器函数Paginator的基本语法

      Paginator类的作用是将我们需要分页的数据分割成若干份,当我们实现一个Paginator类的实例时,需要给其传入参数,我们点到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
    

      根据定义我们可以做如下解释,(上述代码没有将其类属性和方法贴出来):

    • object_list:可以是列表,元组,查询集或者其他含有count()或者 __len()__方法的可切片对象。对于连续的分页,查询集应该有序,例如有order_by()项或者默认ordering参数。
    • per_page:每一页中包含条目数目的最大值,不包括独立成页的那页。
    • orphans=0:当你使用此参数时说明你不希望最后一页只有很少的条目。如果最后一页的条目数少于等于orphans的值,则这些条目会被归并到上一页中(此时的上一页变成最后一页)。例如有23项条目,per_page=10,orphans=0,则由三页,分别为10,10,3,如果orphans>=3,则为2页,分别为10, 13。
    • allow_empty_first_page=True:表示默认允许第一页为空

      一般情况,我们只需传入两个参数。第一个参数是数据源,可以是列表,元组,以及查询集。第二个参数需要传入一个整数,表示每页显示数据条数。

    1.1  Paginator类的方法

    • Paginator.page(number):根据参数number返回一个Page对象(number为1的倍数)

      使用如下:

    #第1页的page对象
    page1=paginator.page(1) 
    
    for i in page1:         #遍历第1页的所有数据对象
        print(i)
    
    print(page1.object_list) #第1页的所有数据
    
    #第2页的page对象
    page2=paginator.page(2)
    

      

    1.2   Paginator类的属性

    • Paginator.count:所有页面对象总数,即统计 object_list 中 item 数目,当计算 object_list 所含对象的数量时,Paginator 会首先尝试调用 object_list.count()。如果 object_list没有 count()方法,Paginator接着会回退使用 Len(object_list)。
    • Paginator.num_pages:页面总数
    • Paginator.page_range:页码范围(页码列表),从1开始(列表是顾头不顾尾),例如[1,2,3,4]。

      上面三个属性是我们Paginator类中常用的属性。我们可以打印其属性对应的值:

    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)  #页码的列表

    2,Page对象的基本语法

      我们通常不用手动创建Page对象,可以从Paginator类来获取。Paginator类提供一个 **page(number)** 函数,该函数返回的是一个Page对象。参数number表示第几个分页。如果number=1,那么page() 返回的对象是第一分页的Page对象。在前端页面中显示数据,我们主要的操作都是基于Page对象。具体的定义如下:

    class Page(collections.Sequence):
    
        def __init__(self, object_list, number, paginator):
            self.object_list = object_list
            self.number = number
            self.paginator = paginator
    

    2.1  page对象的方法

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

      上面加粗的就是我们常用的 page对象方法:

    page2=paginator.page(2)
    
    print(page2.has_next())            #是否有下一页
    print(page2.next_page_number())    #下一页的页码
    print(page2.has_previous())        #是否有上一页
    print(page2.previous_page_number()) #上一页的页码
    

      

    2.2  page对象的属性

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

    2.3  page对象的用法

      下面举个例子:

    #  使用 Paginator 对象返回第一页的  page 对象
    
    books = paginator.page(1)
    

      这就是当number=1的时候,page()返回的对象就是第一分页的Page对象。

    3,非法页码的处理

      InvalidPage(Exception): 异常的基类,当paginator传入一个无效的页码时抛出。

      Paginator.page()放回在所请求的页面无效(比如不是一个整数)时,或者不包含任何对象时抛出异常。通常,捕获InvalidPage异常就够了,但是如果你想更加精细一些,可以捕获以下两个异常之一:

    • exception PageNotAnInteger当向page()提供一个不是整数的值时抛出。
    • exception EmptyPage当向page()提供一个有效值,但是那个页面上没有任何对象时抛出。

           这两个异常都是InalidPage的子类,所以可以通过简单的try - except InvalidPage来处理它们。

    try:
        print(page)
        book_list = paginator.page(page)
    except PageNotAnInteger:
        book_list = paginator.page(1)
    except EmptyPage:
        book_list = paginator.page(paginator.num_pages)
    

      

    4,对Paginator类中函数的简单练习

    分页是Web应用常用的手法,Django提供了一个分页器类Paginator(django.core.paginator.Paginator),可以很容易的实现分页的功能。
    该类有两个构造参数,一个是数据的集合,另一个是每页放多少条数据。
    
    Paginator的基本使用如下:
    
    $python manage.py shell
    
    >>> from django.core.paginator import Paginator
    
    >>> objects = ['john', 'paul', 'george', 'ringo']
    
    >>> p = Paginator(objects, 2)      #每页两条数据的一个分页器
    
    >>> p.count        #数据总数
    
    4
    
    >>> p.num_pages      #总页数
    
    2
    
    >>>p.page_range       #页码的列表
    
    [1, 2]
    
    >>> page1 = p.page(1)     #第1页
    
    >>> page1
    
    <Page 1 of 2>
    
    >>> page1.object_list     #第1页的数据
    
    ['john', 'paul']
    
    >>> page2 = p.page(2)
    
    >>> page2.object_list      #第2页的数据
    
    ['george', 'ringo']
    
    >>> page2.has_next()     #是否有后一页
    
    False
    
    >>> page2.has_previous()   #是否有前一页
    
    True
    
    >>> page2.has_other_pages()   #是否有其他页
    
    True
    
    >>> page2.next_page_number()  #后一页的页码
    
    3
    
    >>> page2.previous_page_number()  #前一页的页码
    
    1
    
    >>> page2.start_index()   # 本页第一条记录的序数(从1开始)
    
    3
    
    >>> page2.end_index()    # 本页最后录一条记录的序数(从1开始)
    
    4
    
    >>> p.page(0)               #错误的页,抛出异常
    
    ...EmptyPage: That page number is less than 1
    
    >>> p.page(3)              #错误的页,抛出异常
    
    ...EmptyPage: That page contains no results
    
    
    其实前面scaffold生成的内容里面就已经包含了分页的功能,相信有了对Paginator的了解,
    你自己就可以看懂在view函数和模板中如何使用分页器了。
    

      

    5, 一个简单的示例

    基本代码如下:
    
    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    from django.core.paginator import Paginator,InvalidPage,EmptyPage,PageNotAnInteger
    
    
    def index(req):
    
        user_list=["用户"+str(i) for i in range(100)]
    
        user_list=getPage(req,user_list)
    
        return render(req,"index.html",locals())
    
    
    def getPage(req,user_list):
    
        paginator=Paginator(user_list,5)
    
        try:
            current_page=req.GET.get("page",1) #  http://127.0.0.1:8000/index/?page=20
            article_list=paginator.page(current_page)
        except (EmptyPage,InvalidPage,PageNotAnInteger):
            article_list=paginator.page(1)
    
        return user_list
    
    
    #*******----------index.html
    {% for i in user_list %}
        <p>{{ i }}</p>
    {% endfor %}
      
    

      

    分页功能的制作过程

    1,完成分页功能的总体思路

    下面分页学习的总体思路分为五步。

    • 1,给数据库插入很多数据,然后保证可以进行分页
    • 2,完成简单的前端分页样式,然后我们可以看到分页的雏形
    • 3,完成点击数字页面都可以进入对应页面的功能
    • 4,完成上一页,下一页可以进入对应页面的功能
    • 5,如果一页内总页数超出默认页数,我们将其限制在10内

    2,为分页准备数据

    2.1,创建一个数据模型

      首先,我们需要创建一个测试项目,我这里将其称为 pageDemo。然后创建一个app,我这里叫做app01。

      下面对于models.py的代码:

    from django.db import models
    
    # Create your models here.
    
    class Book(models.Model):
        title = models.CharField(max_length=32)
        price = models.DecimalField(decimal_places=2, max_digits=8)
    

      将数据库进行迁移。

    python  manage.py makeigirations
    
    python manage.py migrate
    

      

    2.2,向数据库中添加数据

      添加数据的时候,我们不能这样添加

    # Create your views here.
    def index(request):
        for i in range(100):
            Book.objects.create(title="book_%s" % i, price=i * i)
    
        return render(request, 'index.html')
    

      这样虽然可以添加成功,但是每插入一条数据,都要访问一次数据库,要插入100次,要是插入成千上万次,数据库也受不了啊,这是性能问题。所以,我们通过一个Django中的特性 bulk_create()方法批量导入。

      django.db.models.query.QuerySet.bulk_create() 批量创建对象,减少SQL查询的次数。这里不多讲,下面直接使用

      view视图中批量导入数据的代码:

    def index(request):
        # 批量导入
        book_list = []
        for i in range(100):
            book = Book(title='book_%s' %i, price=i*i)
            book_list.append(book)
        Book.objects.bulk_create(book_list)
        return HttpResponse("OK")
        
    

      上面代码中,我们插入了100条数据,我们的方法是创建那么多的数据,先将其保存到一个列表中,然后再将其批量存在数据库中。

      然后开启项目,我们进入网页测试,当出现下面结果,则成功:

       这里简单说一下,我们打印 book,我们会发现效果如下:

     当我们在model表里,添加显示字段的时候,也就是下面代码:

       则出现效果如下:

      查看数据库中Book表里面的数据(因为上面添加了两遍,所以是200多数据):

       添加完之后就将添加数据的代码注释掉了。

    2.3,后端查看插入的数据状况

      这里我们使用ORM获取数据库中所有的数据,然后使用Paginator将每页数据存为10条,也就是每一页显示的数据条数为10,这里我们可以修改。

        book_list = Book.objects.all()
    
        # Paginator 分页器需要两个参数
        paginator = Paginator(book_list, 10)
        # 数据总数
        print('count', paginator.count)
        # 总页数
        print('num_pages', paginator.num_pages)
        # 页码的列表
        print("page_range", paginator.page_range)

      结果如下:

    count 200
    num_pages 20
    page_range range(1, 21)

      从结果来看,总共有200条数据,这里总共有20页数据,页码列表为[1,21]。(因为列表是顾头不顾尾,所以正常)

    2.4,前端页面展示分页的情况

      展示所有的内容的前端代码如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        {% for item in book_list %}
            <li>{{ item }}</li>
        {% endfor %}
    </body>
    </html>

      效果如下:

      显示第一页的两种方式(其实是显示具体某一页的方法):

        # 显示第一页 的数据的两种方式
        page1 = paginator.page(1)
        print(page1.object_list)
        for i in page1:
            print(i)
    

      拿到当前页的视图函数代码:

        current_page = int(request.GET.get('page', 1))
        current_page = paginator.page(current_page)
    

      完整代码如下:

    def index(request):
        book_list = Book.objects.all()
        # paginator 分页器需要两个参数,一个object_list  一个 per_page
        paginator = Paginator(book_list, 10)
    
        # 去前端拿到对应的页数
        current_page_num = int(request.GET.get('page', 1))
        current_page = paginator.page(current_page_num)
    
        # 显示某一页具体数据的两种方式
        print("object_list", current_page.object_list)
        for i in current_page:
            print(i)
    
        return render(request, 'index.html', locals())
    

      当然我们的前端也要修改变量:

        {% for item in current_page %}
            <li>{{ item }}</li>
        {% endfor %}

      查看第一页的效果图:

      查看第二页,或者n页:(只需要设置?page=n即可)

       但是如果超出总页数的话,会报出异常,或者page传来的数据是一个负数(比如-1,-2等),也是会报错。

       这里我们在view视图函数中捕获异常:

        # 如果输出的页面大于总页数的话,可以加上异常捕获
        try:
            current_page = int(request.GET.get('page', 1))
            current_page = paginator.page(current_page)
            # 显示某一页具体数据的两种方式
            for i in current_page:
                print(i)
        except EmptyPage as e:
            current_page = paginator.page(1)
        except PageNotAnInteger:
            current_page = paginator.page(1)
    

     测试一下:

    3,完善前端分页按钮功能

      我们的分页效果其实已经起步完成了,但是用户肯定不会每次还要输入 ?page=n 来跳转到想要去的页面把。这里我们就需要做一个按钮,可以点击对应页面到对应的页数获取对应的数据,比如博客园的:

       下面我们做这个功能。

      为了方便起见,我们使用了Bootstrap的CDN,这里我们直接使用Bootstrap里面的分页组件,进行简单的操作。

    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

      下面是Bootstrap组件中一个简单的分页代码:

    <nav aria-label="Page navigation">
      <ul class="pagination">
        <li>
          <a href="#" aria-label="Previous">
            <span aria-hidden="true">«</span>
          </a>
        </li>
        <li><a href="#">1</a></li>
        <li><a href="#">2</a></li>
        <li><a href="#">3</a></li>
        <li><a href="#">4</a></li>
        <li><a href="#">5</a></li>
        <li>
          <a href="#" aria-label="Next">
            <span aria-hidden="true">»</span>
          </a>
        </li>
      </ul>
    </nav>
    

      修改一下,代码如下:

    <nav aria-label="Page navigation">
      <ul class="pagination">
        <li>
          <a href="#" aria-label="Previous">
            <span aria-hidden="true">上一页</span>
          </a>
        </li>
    
          {% for foo in paginator.page_range %}
            <li><a href="#">{{ foo }}</a></li>
          {% endfor  %}
    
        <li>
          <a href="#" aria-label="Next">
            <span aria-hidden="true">下一页</span>
          </a>
        </li>
      </ul>
    </nav>
    

      结果是这样的:

      虽然此时的效果已经完成,但是各个标签不能点击。下面我们继续完善。

    4,点击1-10都进入对应的页面

      这时候,我们后端代码不需要改变,只需要修改前端代码即可。

        {% for item in paginator.page_range %}
            <li><a href="?page={{ item }}">{{ item }}</a></li>
          {% endfor %}
    

      这里前端中href属性,如果页面中有变量,我们只需要修改?以后的内容即可,前面的默认不动。

      然后点击每一页就出现对应的数据(我这里点击了9):

      我们发现点击9,虽然跳到了9这一页,但是我们从分页中就看不出来,所以我们希望点击9的时候,9的颜色加粗,或者说变为深色。如何做呢?

      这里我们调用bootstrap中的class属性 active。下面修改代码:

    <ul>
        {% for item in current_page %}
            <li>{{ item }}</li>
        {% endfor %}
    </ul>
    
    <nav aria-label="Page navigation">
        <ul class="pagination">
            <li>
                <a href="#" aria-label="Previous">
                    <span aria-hidden="true">上一页</span>
                </a>
            </li>
    
            {% for item in paginator.page_range %}
                {% if current_page_num == item %}
                    <li class="active"><a href="?page={{ item }}">{{ item }}</a></li>
                {% else %}
                    <li><a href="?page={{ item }}">{{ item }}</a></li>
                {% endif %}
            {% endfor %}
    
            <li>
                <a href="#" aria-label="Next">
                    <span aria-hidden="true">下一页</span>
                </a>
            </li>
        </ul>
    </nav>
    

      修改后的效果如下:

      这里我们点击每一页的时候,在href标签中动态的传输每一页的页码,这里就可以访问1-10中每一页的页面。

      注意:这里我们需要注意的是,当在url中输入 ?page=333的时候,也就是不存在的页码的时候,会报错,所以我们使用if - else 进行修改。如果不存在的时候就默认使用第一页即可。

      此时,我们点击1-10就可以进入对应的页面了,下面完成点击上一页,下一页进入对应的页面的功能。

    5,点击上一页下一页进入对应的页面

      点击上一页,下一页进入对应的页面其实不难,但是需要注意的问题就是当上一页或者下一页没有数据的时候,我们需要进行处理

      我们之前学过page对象的方法,在这里就可以使用。下面为了方便列出来需要用的:

    • Page.has_next():如果有下一页,则返回True。
    • Page.has_previous():如果有上一页,则返回True。
    • Page.next_page_number():返回下一页的页面。如果下一页不存在,抛出InvalidPage异常。
    • Page.previous_page_number():返回上一页的页面。如果上一页不存在,抛出InvalidPage异常

      下面修改前端代码,加入if-else判断:

    <ul>
        {% for item in current_page %}
            <li>{{ item }}</li>
        {% endfor %}
    </ul>
    
    <nav aria-label="Page navigation">
        <ul class="pagination">
            {% if current_page.has_previous %}
                <li>
                    <a href="?page={{ current_page.previous_page_number }}" aria-label="Previous">
                        <span aria-hidden="true">上一页</span>
                    </a>
                </li>
            {% else %}
                <li class="disabled">
                    <a href="#" aria-label="Previous">
                        <span aria-hidden="true">上一页</span>
                    </a>
                </li>
            {% endif %}
    
            {% for item in paginator.page_range %}
                {% if current_page_num == item %}
                    <li class="active"><a href="?page={{ item }}">{{ item }}</a></li>
                {% else %}
                    <li><a href="?page={{ item }}">{{ item }}</a></li>
                {% endif %}
            {% endfor %}
    
            {% if current_page.has_next %}
                <li>
                    <a href="?page={{ current_page.next_page_number }}" aria-label="Next">
                        <span aria-hidden="true">下一页</span>
                    </a>
                </li>
            {% else %}
                <li class="disabled">
                    <a href="#" aria-label="Previous">
                        <span aria-hidden="true">下一页</span>
                    </a>
                </li>
            {% endif %}
        </ul>
    </nav>
    

      这样当点第一页的时候,再点上一页,则无法点击了。  这里采用了 disabled。

    6,如果一页内总页数超出默认页数,我们将其限制在10内

    6.1 描述问题

      可能没有描述清楚,下面我们使用图和代码描述。  

      当每页显示3条数据的时候, view视图函数如下:

    # Paginator 分页器需要两个参数
        paginator = Paginator(book_list, 3)
    

      前端代码如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
        <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    
    </head>
    <body>
    
    <ul>
        {% for book in current_page %}
            <li>{{ book.title }}-----------{{ book.price }}</li>
        {% endfor %}
    </ul>
    
    
    
    
    <nav aria-label="Page navigation">
      <ul class="pagination">
      {% if current_page.has_previous %}
          <li><a href="?page={{ current_page.previous_page_number }}" aria-label="Previous">
              <span aria-hidden="true">上一页</span></a></li>
          {% else %}
          <li class="disabled"><a href="" aria-label="Next">
              <span aria-hidden="true">上一页</span></a></li>
          {% endif %}
    
    
          {% for foo in paginator.page_range %}
          {% if current_page_num == foo %}
            <li class="active"><a  href="?page={{ foo }}">{{ foo }}</a></li>
          {% else %}
            <li><a href="?page={{ foo }}">{{ foo }}</a></li>
          {% endif %}
          {% endfor  %}
    
          {% if current_page.has_next %}
          <li><a href="?page={{ current_page.next_page_number }}" aria-label="Next">
              <span aria-hidden="true">下一页</span></a></li>
          {% else %}
          <li class="disabled"><a href="" aria-label="Next">
              <span aria-hidden="true">下一页</span></a></li>
          {% endif %}
      </ul>
    </nav>
    
    </body>
    </html>
    

      所以效果如下:

    6.2  修改并设置限定

      因为这样不好看,甚至说严重不好看,所以我们这里来将其限制到10内。

      在view视图函数中,我们可以判断num_page的大小,如果大于10,则设置范围,如果小于10,则正常即可。而大于10 的时候需要注意一个问题,那就是会出现-1或者大于页码范围的数,我们这里需要捕获,并将其修改。代码如下:

        current_page_num = int(request.GET.get('page', 1))
    
        if paginator.num_pages > 11:
            if current_page_num-5 < 1:
                page_range = range(1, 11)
            elif current_page_num + 5 > paginator.num_pages:
                page_range = range(paginator.num_pages-10, paginator.num_pages + 1)
            else:
                page_range = range(current_page_num-5, current_page_num+6)
        else:
           page_range = paginator.page_range
    

      前端只需要将Paginator.page_range修改为我们后端设置的page_range变量。

          {% for foo in page_range %}
          {% if current_page_num == foo %}
            <li class="active"><a  href="?page={{ foo }}">{{ foo }}</a></li>
          {% else %}
            <li><a href="?page={{ foo }}">{{ foo }}</a></li>
          {% endif %}
          {% endfor  %}
    

      这样得到的效果如下:

      即使他每页只显示3条数据,即使总页数很多,但是我们每页还是只显示10个页面。

    7,完整的Django内置分页代码

    7.1,Django内置分页

      views.py

    from django.shortcuts import render
    from .models import Book
    from django.core.paginator import Paginator, EmptyPage
    
    
    # Create your views here.
    def index(request):
        # insert data to sqlite  not recommend
        # for i in range(100):
        #     Book.objects.create(title="book_%s" % i, price=i * i)
    
        # insert data to sqlite  recommend
        # finish to append data annotation the code
        # book_list = []
        # for i in range(100):
        #     book = Book(title="book_%s" % i, price=i * 2)
        #     print(book)
        #     book_list.append(book)
        #
        # Book.objects.bulk_create(book_list)
    
        book_list = Book.objects.all()
        # paginator 分页器需要两个参数,一个object_list  一个 per_page
        paginator = Paginator(book_list, 10)
        # 数据总数
        print("count", paginator.count)
        # 总页数
        print('num_pages', paginator.num_pages)
        # 页码的列表范围
        print('page_range', paginator.page_range)
    
        # 去前端拿到对应的页数
        current_page_num = int(request.GET.get('page', 1))
    
        if paginator.num_pages > 1:
            if current_page_num - 5 < 1:
                # 这里写的(1, 11) 是我们要显示10个按钮
                page_range = range(1, 12)
            elif current_page_num + 5 > paginator.num_pages:
                page_range = range(paginator.num_pages - 10, paginator.num_pages + 1)
            else:
                # range 也是顾头不够尾
                page_range = range(current_page_num - 5, current_page_num + 6)
        else:
            page_range = paginator.page_range
    
        try:
            current_page = paginator.page(current_page_num)
    
            # 显示某一页具体数据的两种方式
            print("object_list", current_page.object_list)
            for i in current_page:
                print(i)
        except EmptyPage as e:
            # 如果出现的是负数,或者大于页码的数,我们默认让其显示第一页
            current_page = paginator.page(1)
    
        return render(request, 'index.html', locals())
    

      

      html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
              integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    </head>
    <body>
    <ul>
        {% for item in current_page %}
            <li>{{ item }}</li>
        {% endfor %}
    </ul>
    
    <nav aria-label="Page navigation">
        <ul class="pagination">
            {% if current_page.has_previous %}
                <li>
                    <a href="?page={{ current_page.previous_page_number }}" aria-label="Previous">
                        <span aria-hidden="true">上一页</span>
                    </a>
                </li>
            {% else %}
                <li class="disabled">
                    <a href="#" aria-label="Previous">
                        <span aria-hidden="true">上一页</span>
                    </a>
                </li>
            {% endif %}
    
            {% for item in page_range %}
                {% if current_page_num == item %}
                    <li class="active"><a href="?page={{ item }}">{{ item }}</a></li>
                {% else %}
                    <li><a href="?page={{ item }}">{{ item }}</a></li>
                {% endif %}
            {% endfor %}
    
            {% if current_page.has_next %}
                <li>
                    <a href="?page={{ current_page.next_page_number }}" aria-label="Next">
                        <span aria-hidden="true">下一页</span>
                    </a>
                </li>
            {% else %}
                <li class="disabled">
                    <a href="#" aria-label="Previous">
                        <span aria-hidden="true">下一页</span>
                    </a>
                </li>
            {% endif %}
        </ul>
    </nav>
    </body>
    </html>
    

      

    7.2,扩展内置分页 

      views.py

    from django.shortcuts import render
    from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
    
    
    class CustomPaginator(Paginator):
        def __init__(self, current_page, max_pager_num, *args, **kwargs):
            """
            :param current_page: 当前页
            :param max_pager_num:最多显示的页码个数
            :param args:
            :param kwargs:
            :return:
            """
            self.current_page = int(current_page)
            self.max_pager_num = max_pager_num
            super(CustomPaginator, self).__init__(*args, **kwargs)
    
        def page_num_range(self):
            # 当前页面
            # self.current_page
            # 总页数
            # self.num_pages
            # 最多显示的页码个数
            # self.max_pager_num
            print(1)
            if self.num_pages < self.max_pager_num:
                return range(1, self.num_pages + 1)
            print(2)
            part = int(self.max_pager_num / 2)
            if self.current_page - part < 1:
                return range(1, self.max_pager_num + 1)
            print(3)
            if self.current_page + part > self.num_pages:
                return range(self.num_pages + 1 - self.max_pager_num, self.num_pages + 1)
            print(4)
            return range(self.current_page - part, self.current_page + part + 1)
    
    
    L = []
    for i in range(999):
        L.append(i)
    
    def index(request):
        current_page = request.GET.get('p')
        paginator = CustomPaginator(current_page, 11, L, 10)
        # per_page: 每页显示条目数量
        # count:    数据总个数
        # num_pages:总页数
        # page_range:总页数的索引范围,如: (1,10),(1,200)
        # page:     page对象
        try:
            posts = paginator.page(current_page)
            # has_next              是否有下一页
            # next_page_number      下一页页码
            # has_previous          是否有上一页
            # previous_page_number  上一页页码
            # object_list           分页之后的数据列表
            # number                当前页
            # paginator             paginator对象
        except PageNotAnInteger:
            posts = paginator.page(1)
        except EmptyPage:
            posts = paginator.page(paginator.num_pages)
    
        return render(request, 'index.html', {'posts': posts})
    
    扩展内置分页:views.py
    

      

      Html

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    
    <ul>
        {% for item in posts %}
            <li>{{ item }}</li>
        {% endfor %}
    </ul>
    
    <div class="pagination">
    <span class="step-links">
    {% if posts.has_previous %}
        <a href="?p={{ posts.previous_page_number }}">Previous</a>
    {% endif %}
    
        {% for i in posts.paginator.page_num_range %}
            <a href="?p={{ i }}">{{ i }}</a>
        {% endfor %}
    
        {% if posts.has_next %}
            <a href="?p={{ posts.next_page_number }}">Next</a>
        {% endif %}
    </span>
    
    <span class="current">
    Page {{ posts.number }} of {{ posts.paginator.num_pages }}.
    </span>
    
    </div>
    </body>
    </html>
    
    扩展内置分页:Html
    

      

    8、自定义分页

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

    • 1、设定每页显示数据条数
    • 2、用户输入页码(第一页、第二页...)
    • 3、设定显示多少页号
    • 4、获取当前数据总条数
    • 5、根据设定显示多少页号和数据总条数计算出,总页数
    • 6、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置
    • 7、在数据表中根据起始位置取值,页面上输出数据
    • 8、输出分页html,如:[上一页][1][2][3][4][5][下一页]

    8.1  分页实例

    #!/usr/bin/env python
    # _*_coding:utf-8_*_
    from django.utils.safestring import mark_safe
     
    class PageInfo(object):
        def __init__(self,current,totalItem,peritems=5):
            self.__current=current
            self.__peritems=peritems
            self.__totalItem=totalItem
        def From(self):
            return (self.__current-1)*self.__peritems
        def To(self):
            return self.__current*self.__peritems
        def TotalPage(self):  #总页数
            result=divmod(self.__totalItem,self.__peritems)
            if result[1]==0:
                return result[0]
            else:
                return result[0]+1
     
    def Custompager(baseurl,currentPage,totalpage):  #基础页,当前页,总页数
        perPager=11
        #总页数<11
        #0 -- totalpage
        #总页数>11
            #当前页大于5 currentPage-5 -- currentPage+5
                #currentPage+5是否超过总页数,超过总页数,end就是总页数
            #当前页小于5 0 -- 11
        begin=0
        end=0
        if totalpage <= 11:
            begin=0
            end=totalpage
        else:
            if currentPage>5:
                begin=currentPage-5
                end=currentPage+5
                if end > totalpage:
                    end=totalpage
            else:
                begin=0
                end=11
        pager_list=[]
        if currentPage<=1:
            first="<a href=''>首页</a>"
        else:
            first="<a href='%s%d'>首页</a>" % (baseurl,1)
        pager_list.append(first)
     
        if currentPage<=1:
            prev="<a href=''>上一页</a>"
        else:
            prev="<a href='%s%d'>上一页</a>" % (baseurl,currentPage-1)
        pager_list.append(prev)
     
        for i in range(begin+1,end+1):
            if i == currentPage:
                temp="<a href='%s%d' class='selected'>%d</a>" % (baseurl,i,i)
            else:
                temp="<a href='%s%d'>%d</a>" % (baseurl,i,i)
            pager_list.append(temp)
        if currentPage>=totalpage:
            next="<a href='#'>下一页</a>"
        else:
            next="<a href='%s%d'>下一页</a>" % (baseurl,currentPage+1)
        pager_list.append(next)
        if currentPage>=totalpage:
            last="<a href=''>末页</a>"
        else:
            last="<a href='%s%d'>末页</a>" % (baseurl,totalpage)
        pager_list.append(last)
        result=''.join(pager_list)
        return mark_safe(result)   #把字符串转成html语言
    

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

    • 创建处理分页数据的类
    • 根据分页数据获取数据
    • 输出分页HTML,即:[上一页][1][2][3][4][5][下一页]

    9,自定义分页的实例

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    from django.core.paginator import Paginator,InvalidPage,EmptyPage,PageNotAnInteger
    
    
    def index(req):
    
        user_list_all=["用户"+str(i) for i in range(1000)]
        #需要分页显示
    
        current_page=int(req.GET.get("page",1))
    
        # start=(current_page-1)*5
        # end=(current_page-1)*5+5
        start=(current_page-1)*10
        end=(current_page-1)*10+10
    
        user_list=user_list_all[start:end]
        #不能让用户去url写page,所以写入Page Number
        #问题是:显示多少页码(Page Number)?
    
        total_item=len(user_list_all)
        pageNumber,remaining=divmod(total_item,10)  #每页显示10条数据
        if remaining>0:
            pageNumber+=1
    
        list_tag=[]
    
        #默认最多显示10页码
        PN=6
        half_PN=int(PN/2)
        if pageNumber<PN:
            BEGIN=0
            END=pageNumber
        else:
            if current_page>half_PN:
    
                if current_page<(pageNumber-half_PN):
                    BEGIN=current_page-half_PN
                    END=current_page+half_PN
                else:
                    #最后几页不需要再增加新的页码
                    BEGIN=pageNumber-PN
                    END=pageNumber
            else:
                BEGIN=0
                END=PN
    
        baseurl='/index/?page='
    
        if current_page<=1:
            first="<a href='#'>首页</a>"
        else:
            first="<a href='%s%d'>首页</a>" % (baseurl,1)
        list_tag.append(first)
    
        if current_page<=1:
            prev="<a href=''>上一页</a>"
        else:
            prev="<a href='%s%d'>上一页</a>" % (baseurl,current_page-1)
        list_tag.append(prev)
    
        for i in range(BEGIN+1,END+1):
            if i == current_page:
                temp="<a href='%s%d' class='active'>%d</a>" % (baseurl,i,i)
            else:
                temp="<a href='%s%d'>%d</a>" % (baseurl,i,i)
            list_tag.append(temp)
        if current_page>=pageNumber:
            next="<a href='#'>下一页</a>"
        else:
            next="<a href='%s%d'>下一页</a>" % (baseurl,current_page+1)
        list_tag.append(next)
        if current_page>=pageNumber:
            last="<a href='#'>末页</a>"
        else:
            last="<a href='%s%d'>末页</a>" % (baseurl,pageNumber)
    
        list_tag.append(last)
    
        tags="".join(list_tag)
    
        return render(req,"index.html",locals())
    
    
    
    #------------------------------------index.html
    #------------------------------------
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            .pager a{
                display: inline-block;
                 60px;
                height: 20px;
                padding: 5px;
                background-color: darkgrey;
                color: #2459a2;
                text-decoration: none;
                text-align: center;
                line-height: 20px;
            }
            .pager a.active{
                color: white;
                background-color: red;
            }
        </style>
    </head>
    <body>
    
    
    {% for user in user_list %}
        <p>{{ user }}</p>
    {% endfor %}
    <div class="pager">
        {{ tags|safe }}
    </div>
    
    </body>
    </html>
    

      

    10,自己写一个分页组件

      这个组件不依赖于任何东西,使用的时候可以直接调用,代码如下:

      pagination.py

    """
    分页组件
    """
    
    
    class Pagination(object):
        def __init__(self, current_page, all_count, base_url, query_params, per_page=20, pager_page_count=11):
            """
            分页初始化
            :param current_page: 当前页码
            :param per_page: 每页显示数据条数
            :param all_count: 数据库中总条数
            :param base_url: 基础URL
            :param query_params: QueryDict对象,内部含所有当前URL的原条件
            :param pager_page_count: 页面上最多显示的页码数量
            """
            self.base_url = base_url
            try:
                self.current_page = int(current_page)
                if self.current_page <= 0:
                    raise Exception()
            except Exception as e:
                self.current_page = 1
            self.query_params = query_params
            self.per_page = per_page
            self.all_count = all_count
            self.pager_page_count = pager_page_count
            pager_count, b = divmod(all_count, per_page)
            if b != 0:
                pager_count += 1
            self.pager_count = pager_count
    
            half_pager_page_count = int(pager_page_count / 2)
            self.half_pager_page_count = half_pager_page_count
    
        @property
        def start(self):
            """
            数据获取值起始索引
            :return:
            """
            return (self.current_page - 1) * self.per_page
    
        @property
        def end(self):
            """
            数据获取值结束索引
            :return:
            """
            return self.current_page * self.per_page
    
        def page_html(self):
            """
            生成HTML页码
            :return:
            """
            # 如果数据总页码pager_count<11 pager_page_count
            if self.pager_count < self.pager_page_count:
                pager_start = 1
                pager_end = self.pager_count
            else:
                # 数据页码已经超过11
                # 判断: 如果当前页 <= 5 half_pager_page_count
                if self.current_page <= self.half_pager_page_count:
                    pager_start = 1
                    pager_end = self.pager_page_count
                else:
                    # 如果: 当前页+5 > 总页码
                    if (self.current_page + self.half_pager_page_count) > self.pager_count:
                        pager_end = self.pager_count
                        pager_start = self.pager_count - self.pager_page_count + 1
                    else:
                        pager_start = self.current_page - self.half_pager_page_count
                        pager_end = self.current_page + self.half_pager_page_count
    
            page_list = []
    
            if self.current_page <= 1:
                prev = '<li><a href="#">上一页</a></li>'
            else:
                self.query_params['page'] = self.current_page - 1
                prev = '<li><a href="%s?%s">上一页</a></li>' % (self.base_url, self.query_params.urlencode())
            page_list.append(prev)
            for i in range(pager_start, pager_end + 1):
                self.query_params['page'] = i
                if self.current_page == i:
                    tpl = '<li class="active"><a href="%s?%s">%s</a></li>' % (
                        self.base_url, self.query_params.urlencode(), i,)
                else:
                    tpl = '<li><a href="%s?%s">%s</a></li>' % (self.base_url, self.query_params.urlencode(), i,)
                page_list.append(tpl)
    
            if self.current_page >= self.pager_count:
                nex = '<li><a href="#">下一页</a></li>'
            else:
                self.query_params['page'] = self.current_page + 1
                nex = '<li><a href="%s?%s">下一页</a></li>' % (self.base_url, self.query_params.urlencode(),)
            page_list.append(nex)
            page_str = "".join(page_list)
            return page_str
    

      调用方法如下:

    后端调用

    • - 可以自己设置 per_page_count,组件中的 per_page_count设置为20,我们自己可以让每页获取的数据设置为10,15,20等等。
    • - 获取表数据中的数据总数(all_count = self.model_class.objects.all().count())
    • - 获取 query_params 这是一个QueryDict对象,内部含所有当前URL的原条件——我们使用 request_parmas = request.GET.copy() 复制一份URL地址,然后调用

    调用方法如下:

            # ########## 1. 处理分页 ##########
            all_count = self.model_class.objects.all().count()
            query_params = request.GET.copy()
            query_params._mutable = True
    
            pager = Pagination(
                current_page=request.GET.get('page'),
                all_count=all_count,
                base_url=request.path_info,
                query_params=query_params,
                per_page=self.per_page_count,
            )
            
            data_list = self.model_class.objects.all()[pager.start:pager.end]
    

      同时,我们要将pager传给前端:

            return render(
                request,
                'testchange.html',
                {
                    'pager':pager
                }
            )
    

      

    前端调用

      在前端调用的方法如下:

            <nav>
                <ul class="pagination">
                    {{ pager.page_html|safe }}
                </ul>
            </nav>
    

      

    11,注意:完整代码上传到我的GitHub上

      我的GitHub地址:https://github.com/LeBron-Jian/LuffyCity_Project-StudyNote

      或者:点击传送门 直接打开。注意进去找 pageDemo文件。

    参考文献:https://www.cnblogs.com/king-lps/p/7324821.html

    https://www.cnblogs.com/yuanchenqi/articles/9036515.html

  • 相关阅读:
    函数
    python操作文件
    POJ-2689-Prime Distance(素数区间筛法)
    POJ-2891-Strange Way to Express Integers(线性同余方程组)
    POJ-2142-The Balance
    POJ-1061-青蛙的约会(扩展欧几里得)
    Educational Codeforces Round 75 (Rated for Div. 2) D. Salary Changing
    Educational Codeforces Round 75 (Rated for Div. 2) C. Minimize The Integer
    Educational Codeforces Round 75 (Rated for Div. 2) B. Binary Palindromes
    Educational Codeforces Round 75 (Rated for Div. 2) A. Broken Keyboard
  • 原文地址:https://www.cnblogs.com/wj-1314/p/10620178.html
Copyright © 2011-2022 走看看