小白IT:Django框架—分页器paginator

文章目录

  • 一、Django内置paginator分页器
    • 1.数据准备
          • models.py文件创建表类
        • 在views外使用Django环境创建数据
    • 2.paginator对象常用属性和方法
      • paginator对象属性
      • paginator对象的方法
        • page_obj对象的方法
    • 3.Django分页器实现分页
          • 视图函数部分
          • 分页后的前端页面
  • 二、自定义分页器CustomerPaginator
    • 1.为什么使用自定义分页器
    • 2.自定义分页器实例
      • 自定义分页器组件
      • 自定分页组件使用
        • 1.分页器使用
        • 2.具体使用实例
          • 后端views视图
          • 前端使用分页器
          • 效果图如下

一、Django内置paginator分页器

1.数据准备

models.py文件创建表类
from django.db import models
# Create your models here.

class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    age=models.IntegerField()
    authorDetail=models.OneToOneField(to="AuthorDetail",to_field="nid")
    def __str__(self):
        return self.name
class AuthorDetail(models.Model):

    nid = models.AutoField(primary_key=True)
    birthday=models.DateField()
    telephone=models.BigIntegerField()
    addr=models.CharField( max_length=64)

class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    city=models.CharField( max_length=32)
    email=models.EmailField()
    def __str__(self):
        return self.name

class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField( max_length=32)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)
    publish=models.ForeignKey(to="Publish",to_field="nid")
    authors=models.ManyToManyField(to='Author',)
    def __str__(self):
        return self.title

指定数据库同步指令:makemigrations和migrate

在views外使用Django环境创建数据

在应用文件app01下创建一个py文件,名字随意,如init_book_table.py

import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day068_model_form_lesson.settings")  # 引入django的环境
    import django
    django.setup()  # 运行Django环境,之后可以使用django中的其他文件
    from app01 import models
    import random

    l1 = []
    for i in range(1, 81):  # 创建100条记录
        obj = models.Book(
            title='红楼梦第%s回'%i,
            price= random.randint(100,200),
            publishDate='1998-06-%s'%(random.randint(1,30)),
            publish_id=random.randint(1,3),
        )
        l1.append(obj)
    models.Book.objects.bulk_create(l1)

创建好后,直接右键运行即可,就可以用DJango环境执行这段代码,在数据库中创建100天记录。

2.paginator对象常用属性和方法

使用django内置的分页器需要导入分页器类

from django.core.paginator import Paginator,EmptyError  # EmptyError是一个没有取到页面的错误

通过给Paginator类传递所有数据的queryset对象,以及每页显示数量来实例化一个paginator对象。这个对象有所有对分页的处理方法。

paginator对象属性

paginator.count:返回所有记录的总数量

paginator.num_pages:返回分页后的总页数

paginator.page_range:返回分页后的页码范围,一个range对象

all_books = models.Book.objects.all()
paginator = Paginator(all_books,10)  # 将所有的book对象传给Paginator生成分页器对象,10表示每一页展示的数据数量

"""paginator对象的属性"""
print("count",paginator.count)  # 显示数据总数量 count 83
print("num_pages",paginator.num_pages)  # 显示分页后总页数 num_pages 9
print("page_range",paginator.page_range)  # 显示页面的范围 page_range range(1, 10)

paginator对象的方法

  • page_obj = paginator.page(page):获取某一页所有数据的一个对象
    • 如果页码值不在页码范围内,抛出异常error:EmptyPage 超过页码范围
    • 如果页码值非法:抛出异常error:PageNotAnInteger 非法页码值

page_obj对象的方法

  • page_obj.has_next():判断当前页是否有下一页
  • page_obj.next_page_number():下一页的页码
  • page_obj.has_previous():判断当前页是否有上一页
  • page_obj.previous_page_number():上一页的页码
all_books = models.Book.objects.all()
paginator = Paginator(all_books,10)  # 将所有的book对象传给Paginator生成分页器对象,10表示每一页展示的数据数量

"""paginator对象的属性"""
print("count",paginator.count)  # 显示数据总数量 count 83
print("num_pages",paginator.num_pages)  # 显示分页后总页数 num_pages 9
print("page_range",paginator.page_range)  # 显示页面的范围 page_range range(1, 10)

3.Django分页器实现分页

视图函数部分
from django.shortcuts import render, redirect, HttpResponse
from utils.modelFormLesson import BookForm
from app01 import models
from django.core.paginator import Paginator,EmptyPage
def show(request):
    if request.method == "GET":
        all_books = models.Book.objects.all()
        paginator = Paginator(all_books,10)  # 将所有的book对象传给Paginator生成分页器对象,10表示每一页展示的数据数量

        """paginator对象的属性"""
        print("count",paginator.count)  # 显示数据总数量 count 83
        print("num_pages",paginator.num_pages)  # 显示分页后总页数 num_pages 9
        print("page_range",paginator.page_range)  # 显示页面的范围 page_range range(1, 10)

        # """paginator对象的方法"""
        # page1 = paginator.page(1)  # 获取第一页的所有数据
        # print(page1)
        # for i in page1:
        #     print(i)
        #
        # page_obj = paginator.page(2)
        # print("是否有下一页",page_obj.has_next())  # 是否有上一页 True
        # print("下一页的页码",page_obj.next_page_number())  # 下一页的页码 3
        # print("是否有上一页",page_obj.has_previous())  # 是否有下一页 True
        # print("上一页的页码",page_obj.previous_page_number())  # 上一页的页码 1

        """分页逻辑部分"""
        current_page_num = request.GET.get("page",1)  # 获取请求页面,也就是当前页
        current_page_num = int(current_page_num)
        all_pages = paginator.num_pages  # 分页后的所有页数
        try:
            current_books = paginator.page(current_page_num)  # 获取当前页的所有数据
        except EmptyPage:
            current_books = paginator.page(paginator.num_pages)
        print(">>>>>>>>>>",current_books)

        if current_page_num <= 3:
            page_range = range(1,6)
        elif 3 < current_page_num <= paginator.num_pages - 2:
            page_range = range(current_page_num-2,current_page_num+3)
        else:
            page_range = range(all_pages-4,all_pages+1)
        return render(request,"show.html",{"page_range":page_range,"current_books":current_books,"current_page_num":current_page_num})

模板文件show.html

在前端页面注意点:

在实现上一页和下一页的功能时,需要判断当前页是不是最后一页,也就是当前页有没有上一页或下一页。

如果是,那么在a标签中的href属性中不能再使用current_books.next_page_number或current_books.previous_page_number,不然会EmptyError错误。

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
</head>
<body>
<h1 style="text-align: center">书籍展示页面</h1>
<div class="container-fluid">
    <div class="row">
        <div class="col-sm-8 col-sm-offset-2">

            <!--===============================数据展示部分==============================-->
            <div style="height: 450px">
                <table class="table table-hover table-striped">
                    <thead>
                    <tr>
                        <th>序号</th>
                        <th>书名</th>
                        <th>出版日期</th>
                        <th>价格</th>
                        <th>出版社</th>
                        <th>作者</th>
                        <th>菜单</th>
                    </tr>
                    </thead>
                    <tbody>
                    {% for book in current_books %}
                        <tr>
                            <td>{{ forloop.counter }}</td>
                            <td>{{ book.title }}</td>
                            <td>{{ book.publishDate|date:"Y-m-d" }}</td>
                            <td>{{ book.price }}</td>
                            <td>{{ book.publish }}</td>
                            <td>
                                {% for author in book.authors.all %}
                                    {{ author.name }}
                                    {% if forloop.last %}

                                    {% endif %}
                                {% endfor %}
                            </td>
                            <td>
                                <a href="" class="btn btn-warning btn-xs">编辑</a>
                                <button class="btn btn-danger btn-xs">删除</button>
                                <span class="hidden">{{ book.pk }}</span>
                            </td>
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
            </div>
            <!--===============================分页部分==================================-->
            <nav aria-label="Page navigation" class="pull-right">
                <ul class="pagination">
                <!--上一页设置-->
                    <!--判断是否有上一页,有的话可以点击左箭头跳上一页-->
                    {% if current_books.has_previous %}
                        <li>
                            <a href="{% url 'show' %}?page={{ current_books.previous_page_number }}"
                               aria-label="Previous">
                                <span aria-hidden="true">&laquo;</span>
                            </a>
                        </li>
                        <!--没有上一页,让按钮变为disable,不让点击-->
                    {% else %}
                        <li class="disabled">
                            <a aria-label="Previous">  <!--注意第一页没有上一页,所以这里a标签的href属性不能再写了,如果写了,那么current_books.previous_page_number方法会一直报错。-->
                                <span aria-hidden="true">&laquo;</span>
                            </a>
                        </li>
                    {% endif %}

                <!--页面设置-->
                    {% for page in page_range %}
                        {% if page == current_page_num %}
                            <li class="active">
                                <a href="{% url 'show' %}?page={{ page }}" aria-label="Previous">
                                    <span aria-hidden="true">{{ page }}</span>
                                </a>
                            </li>
                        {% else %}
                            <li>
                                <a href="{% url 'show' %}?page={{ page }}" aria-label="Previous">
                                    <span aria-hidden="true">{{ page }}</span>
                                </a>
                            </li>
                        {% endif %}
                    {% endfor %}

                <!--下一页设置-->
                    <!--判断是否有下一页,有的话可以点击右箭头跳下一页-->
                    {% if current_books.has_next %}
                        <li>
                            <a href="{% url 'show' %}?page={{ current_books.next_page_number }}"
                               aria-label="Next">
                                <span aria-hidden="true">&raquo;</span>
                            </a>
                        </li>
                        <!--没有上一页,让按钮变为disable,不让点击-->
                    {% else %}
                        <li class="disabled">
                            <a aria-label="Next">  <!--注意最后一页没有下一页,所以这里a标签的href属性不能再写了,如果写了,那么current_books.next_page_number方法会一直报错。-->
                                <span aria-hidden="true">&raquo;</span>
                            </a>
                        </li>
                    {% endif %}


                </ul>
            </nav>
        </div>
    </div>
</div>

<script src="{% static 'jquery-3.4.1.js' %}"></script>
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
</body>
</html>
分页后的前端页面

小白IT:Django框架—分页器paginator_第1张图片

二、自定义分页器CustomerPaginator

1.为什么使用自定义分页器

django的内置分页器可以实现简单的分页设置,但是如果我们需要涉及到更多细节的设置,更人性化的配置的时候,django内置分页器就无法满足我们的使用需求。

这种场景,我们需要自己定义分页器组件,通过后端传递数据,在分页器组件中,根据分页的业务做逻辑处理,然后拼接成分页器标签返回。拿到分页器标签后,直接render给前端页面,生成在页面中。

2.自定义分页器实例

这里的自定义分页器,仅仅博主本人写的一个简单的自定义分页器,能够解决常用的分页需求。如果无法满足具体需求,可以根据具体需求进行扩展。

自定义分页器组件

项目下新建utils文件夹,用来存放扩展的功能组件,在utils下新建customPaginator.py,分页器组件代码如下:

import copy
class Paginator:
    """自定义分页器"""
    def __init__(self, request, data_counts, per_counts=10, show_counts=5):
        """
        初始属性
        :param request: 请求的所有请求数据
        :param data_counts: 展示的总数据数量
        :param per_counts: 每页展示的数据数量
        :param show_counts: 在前端显示的页码数量
        """
        self.request = request
        self.data_counts = data_counts
        self.per_counts = per_counts
        self.show_counts = show_counts
        self.params = copy.deepcopy(request.GET)  # 获取请求的GET数据的深拷贝querydict可以使用

        # 获取数据分页后总页数
        def page_counts():
            """
            获取总的页码数量
            :return:
            """
            page_counts, mod = divmod(self.data_counts, self.per_counts)
            if mod:
                page_counts += 1
            return page_counts

        # 获取总页数,并添加在self属性中
        self.page_counts = page_counts()

        # 获取当前页页码
        self.current_page = request.GET.get("page", "")

        try:  # 异常处理当前页码是否为数字
            self.current_page = int(self.current_page)
        except Exception:
            self.current_page = 1

        # 判断当前页码在不在合理范围内
        if self.current_page < 1:
            self.current_page = 1
        if self.current_page > self.page_counts:
            self.current_page = 1

    @property  # 装饰为普通属性
    def start(self):
        """
        返回某一页数据展示的开始位置
        :return:
        """
        return (self.current_page - 1) * self.per_counts

    @property  # 装饰为普通属性
    def end(self):
        """
        返回某一页数据展示的结束位置
        :return:
        """
        return (self.current_page) * self.per_counts

    def paginate(self):
        """
        根据数据数据量,每页数据,每页页码,拼接分页标签返回
        :return:返回拼接好的分页器标签
        """
        tag = ""
        page_counts = self.page_counts

        # 设置首页标签url
        self.params["page"] = 1
        params = self.params.urlencode()  # condition=name&q=qwerqw&page=1
        if self.current_page == 1:
            first = f"""
                
  • """
    else: first = f"""
  • """
    # 设置上一页标签的url if self.current_page <= 1: # 如果当前页是第一页,上一页不可选 previous = """
  • """
    else: # 不是第一页,拼接上一页的url路径 self.params["page"] = self.current_page - 1 params = self.params.urlencode() # condition=name&q=qwerqw&page=num previous = f"""
  • """
    # 获取要显示的页码范围 half_num = self.show_counts // 2 if self.page_counts < self.show_counts: # 判断总页数是不是小于要显示的页数 range_start = 1 range_end = self.page_counts else: # 总页数大于要显示的页数 if self.current_page <= half_num + 1: range_start = 1 range_end = self.show_counts elif half_num + 1 < self.current_page < page_counts - half_num: range_start = self.current_page - half_num range_end = self.current_page + half_num else: range_start = page_counts - self.show_counts + 1 range_end = page_counts # 获取中间的页码标签 middle = "" for num in range(range_start, range_end + 1): # 保留查询参数的路径 self.params["page"] = num params = self.params.urlencode() # condition=name&q=qwerqw&page=num # 判断哪一页是当前页,是的话,添加active效果 if self.current_page == num: one_tag = f"""
  • {num}
  • """
    else: one_tag = f"""
  • {num}
  • """
    middle += one_tag # 设置下一页标签的url if self.current_page >= self.page_counts: # 如果当前页已经是最大页数,下一页禁用 next = """
  • """
    else: # 如果不是正常使用 self.params["page"] = self.current_page + 1 params = self.params.urlencode() # condition=name&q=qwerqw&page=num next = f"""
  • """
    # 设置尾页标签url self.params["page"] = self.page_counts params = self.params.urlencode() # condition=name&q=qwerqw&page=1 if self.current_page == self.page_counts: last = f"""
  • """
    else: last = f"""
  • """
    tag = f""" """ return tag def jump_page(self): """ 产生跳转页标签 :return: 返回跳转页的标签 """ url = self.request.get_full_path() if not "/?" in url: url = url+"?page=" else: if "page" in url: url = url.split("page")[0]+"page=" else: url = url + "&page=" jump_tag =f"""
    跳转到 Go!
    """
    return jump_tag def jump_js(self): """ 生成跳转页使用的js代码 :return: """ jump_js = """ """ return jump_js

    自定分页组件使用

    1.分页器使用

    分页器接受参数

    • request: 请求的所有请求数据
    • data_counts: 展示的总数据数量
    • per_counts: 每页展示的数据数量,默认是每页10条
    • show_counts: 在前端显示的页码数量,默认是5页

    分页器方法

    • start():方法根据提供的当前所在页码,返回当前页数据的开始位置
    • end():方法根据提供的当前所在页码,返回当前页数据的结束位置
    • paginate():返回拼接好的分页标签的html文本,在前端使用
    • jump_page():返回拼接好的跳转页标签html文本,在前端使用
    • jump_js():返回跳转页使用对应的js代码,在前端使用

    2.具体使用实例

    后端views视图
    from crmweb import models
    from django import views
    from utils.customPaginator import Paginator  # 导入自定义分页器类
    
    
    # 展示客户数据的视图
    class CommonData(views.View):
        @method_decorator(login_required)  # 验证客户登录状态的装饰器,这里可有可无
        def dispatch(self, request, *args, **kwargs):
            res = super().dispatch(request, *args, **kwargs)
            return res
    
        def get(self, request):
            # 查询要显示的所有数据
            all_customers = models.Customer.objects.filter(consultant__isnull=True, status="unregistered").order_by("-pk")
    
            # 开始分页展示
            data_counts = all_customers.count()  # 获取分页的总数据数量
    
            # 生成一个分页对象
            paginator = Paginator(request, data_counts, 10)
    
            # 获取当前页展示数据的范围
            try:  # 异常是否查到了数据,查到了才切片,不然会报错
                all_customers = all_customers[paginator.start:paginator.end]
            except Exception:
                pass
    
            # 获取分页的标签
            paginator_tag = paginator.paginate()  # 调用定义好的分页方法
    
            # 获取跳转页的标签
            jump_tag = paginator.jump_page()  # 调用定义好的跳转页方法获取跳转页标签
            jump_js = paginator.jump_js()  # 调用定义好的跳转页方法获取跳转页js代码
    
            return render(request, "customers_common_list.html",
                          {"all_customers": all_customers, "paginator_tag": paginator_tag,
                           "jump_tag": jump_tag, "jump_js": jump_js})
    
    前端使用分页器
    <table id="example2" class="table table-bordered table-hover text-center">
        <thead>
        <tr>
            <th style="width: 5%">序号</th>
            <th>qq</th>
            <th>姓名</th>
            <th>电话</th>
            <th>来源</th>
            <th>咨询课程</th>
            <th>客户状态</th>
            <th>销售老师</th>
            <th>跟进信息</th>
            <th>操作</th>
        </tr>
        </thead>
        <tbody>
    
        <tr>
            <td>{{ forloop.counter }}</td>
            <td>{{ customer.qq }}</td>
            <td>{{ customer.qq_name }}</td>
            <td>
                {{ customer.phone|default:"暂无" }}
            </td>
            <td>{{ customer.get_source_display|default:'暂无' }}</td>
            <td>{{ customer.get_course_display|default:"暂无" }}</td>
            <td>{{ customer.get_status_display }}</td>
            <td>{{ customer.consultant.username|default:'暂无' }}</td>
            <td><a href="{% url 'consult_record' customer.pk %}">详情</a></td>
            <td>
    
                <a style="color: #00c3cc;" href="{% url 'consult_record_edit' customer.pk %}">
                    <i class="fa fa-edit" aria-hidden="true"></i>
                </a>
                |
                <a style="color: #d9534f;" href="{% url 'consult_record_del' customer.pk %}">
                    <i class="fa fa-trash-o"></i>
                </a>
            </td>
        </tr>
        </tbody>
    </table>
    
    <!--自定义分页器使用-->
    <div class="pull-right" style="display:inline-block; width: 120px;margin: 22px 10px">
        {{ jump_tag|safe }}
    </div>
    
    
    <!--自定义跳转页使用-->
    <div class="pull-right">
        {{ paginator_tag|safe }}
    </div>
    {{ jump_js|safe }}
    
    效果图如下

    小白IT:Django框架—分页器paginator_第2张图片

    你可能感兴趣的:(Python入门基础,django,pagination,python,web)