分页组件设计

分页组件的设计

在web应用系统中,很多时候需要在页面列出数据库表中的记录。如果表中记录很多。一方面会导致检索时间长,另一方面在一个页面列出全部记录会使页面变得很长。因此要进行分页。

样例8:普通分页编写

在test_orm项目上建立一个test_page应用。

URL配置

首先在终端输入python manage.py startapp test_page建立应用,在settings注册。

在/test_orm/test_orm/urls.py中加入配置:

path('test_page/',include('test_page.urls')),

在/test_orm/test_page/下新建urls,输入:

from django.urls import path,include
from . import views
urlpatterns = [
    # 普通分页页面
    path('person_page/',views.person_page),
    # 用分页组件的页面
    path('person_pagenew/',views.person_pagenew)
]

数据模型

在models中编写,生成一张分页要用的数据库表。

from django.db import models

# 员工数据模型
class person(models.Model):
    # 员工姓名
    name = models.CharField(max_length=32,verbose_name='姓名')
    # 员工的邮箱
    email = models.EmailField(verbose_name='邮箱')
    # 员工的部门
    dep = models.ForeignKey(to='department',to_field='id',on_delete=models.CASCADE)
    # 薪水
    salary = models.DecimalField(max_digits=8,decimal_places=2)
    def __str__(self):
        return self.name
    
class department(models.Model):
    # 部门名称
    dep_name = models.CharField(max_length=32,verbose_name='部门名称',unique=True,blank=False)
    # 部门备注说明
    dep_script = models.CharField(max_length=60,verbose_name='备注',null=True)
    def __str__(self):
        return self.dep_name

通过命令生成数据库表然后输入一定数量的记录供分页测试使用。

视图函数

from django.shortcuts import render,HttpResponse
from . import models
def person_page(request):
    # 从URL中取出参数page,这个参数是"?page=1"形式
    cur_page_num = request.GET.get('page')
    # 取得person中的记录总数
    total_count = models.person.objects.all().count()
    # 设定每一页显示多少条记录
    one_page_lines = 10
    # 页面上总共展示多少页码标签
    page_maxtag = 9
    """
    根据总记录数,计算出总的页数
    通过divmod函数取得商和余数,有余数时,总页数是商加1
    同时判断当前页的页码是否大于总页数,如果大于总页数,设置当前页码等于最后一页的页码
    """
    total_page,remainder=divmod(total_count,one_page_lines)
    if remainder:
        total_page += 1
    try:
        # 参数page传递过来的是字符类型数值,因此需要转化为整数类型
        cur_page_num = int(cur_page_num)
        # 如果输入的页码超过了超过了最大的页码,设置当前页码是最后一页的页码
        if cur_page_num > total_page:
            cur_page_num = total_page
    except Exception as e:
        # 当输入的页码不是正整数或者不是数字时,设置当前页码是第一页的页码
        cur_page_num = 1
    # 定义两个变量,指定表中当前页的记录开始数,以及当前页的记录结束数
    rows_start = (cur_page_num-1)*one_page_lines
    rows_end = cur_page_num*one_page_lines
    # 如果页数小于每页设置的页码标签数,设置每页页码标签数为总页数
    if total_page<page_maxtag:
        page_maxtag=total_page
    # 把当前页码标签放在中间,前面放一半页码标签,后面放一半页码标签
    # 因此先把设置的页码标签数除以2
    half_page_maxtag = page_maxtag//2
    # 页面上页码标签的开始数
    page_start = cur_page_num - half_page_maxtag
    # 页面上页码标签的结束数
    page_end = cur_page_num + half_page_maxtag
    """
    如果计算出的页码标签开始数小于1,页面中页码标签设置从1开始
    设置页面中页码标签结束数等于前面设置的页码标签总数page_maxtag
    """
    if page_start <=1:
        page_start =1 
        page_end=page_maxtag
    """
    如果计算出的页码标签数比总页码数大,设置最后的页码标签数为总页数
    设置页面中页码开始数等于总页数减去page_maxtag加1
    """
    if page_end >= total_page:
        page_end = total_page
        page_start = total_page-page_maxtag + 1
        if page_start <= 1:
            page_start = 1
    # 对person表中的记录进行切片,取出属于本页的记录
    per_list = models.person.objects.all()[rows_start:rows_end]
    # 初始化一个列表变量,用来保存拼接分页的HTML代码
    html_page = []
    # 首页代码
    html_page.append('<li><a href="/test_page/person_page/?page=1">首页</a></li>')
    # 上一页页码标签的HTML代码,如果当前是第一页,设置上一页页码标签为非可用状态
    if cur_page_num <=1:
        html_page.append('<li class="disabled"><a href="#"><span aria-hidden="true">&laquo;</span></a></li>'.format(
            cur_page_num-1))
    else:
        # 上一页页码标签的HTML代码
        html_page.append('<li><a href="/test_page/person_page/?page={}"><span aria-hidden="true">&laquo;</span></a></li>'.format(
            cur_page_num - 1))
    # 依次取页码标签
    for i in range(page_start,page_end+1):
        # 如果等于当前页就加一个active类
        if i == cur_page_num:
            html_temp = '<li class="active"><a href="/test_page/person_page/?page={0}">{0}</a></li>'.format(i)
        else:
            html_temp = '<li><a href="/test_page/person_page/?page={0}">{0}</a></li>'.format(i)
        html_page.append(html_temp)
    # 下一页页码标签的HTML页码
    # 判断如果是最后一页,下一页设为disabled
    if cur_page_num >= total_page:
        html_page.append('<li class="disabled"><a href="#"><span aria-hidden="true">&raquo;</span></a></li>')
    else:
        html_page.append(
            '<li><a href="/test_page/person_page/?page={}"><span aria-hidden="true">&raquo;</span></a></li>'.format(
                cur_page_num + 1))
    # 最后一页页码标签的html代码
    html_page.append(
        '<li><a href="/test_page/person_page/?page={}">尾页</a></li>'.format(total_page))
    # 把HTML连接起来
    page_nav = ''.join(html_page)
    return render(request,'test_page/list_person.html',{'person_list':per_list,'page_nav':page_nav})

说明:

以上代码主要生成一个分页的HTML片段,存在变量中发送给模板文件,主要有五步:

  1. 从URL取得当前页码,对应代码cur_page_num = request.GET.get('page')。
  2. 取得总记录数,指定每页要显示的记录数,每页要显示的页码标签数。
  3. 计算出总页数,计算出当前页中记录从第几条开始到第几条结束并取出这些记录。
  4. 计算出页码标签的起始值和结束值。
  5. 生成分页的HTML代码,存在变量中,通过render函数发给模板文件。

list_person.html:

{% load static %}

<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <title>模板样例</title>
    <!-- Bootstrap core CSS -->
    <link href="{% static 'bootstrap/css/bootstrap.min.css'%}" rel="stylesheet">

</head>

<body>
<div class="col-sm-4 col-sm-offset-4 col-md-6 col-md-offset-3 main">
    <br>
    <br>
    <div class="panel panel-primary">
        <div class="panel-heading">
            <h3 class="panel-title">员工列表</h3> <!--这里加标题 //-->
        </div>
        <div class="panel-body"> <!--将表格放在这个<div class="panel-body">的标签中 //-->

            <table class="table table-bordered table-condensed table-striped table-hover"> <!--给表格增加bootstrap样式 //-->
                <thead>
                <tr>
                    <th>姓名</th>
                    <th>邮件</th>
                    <th>薪水</th>
                    <th>部门</th>
                </tr>
                </thead>
                <tbody>
                {% for per in person_list %}
                <tr>
                    <td>{{ per.name }}</td>
                    <td>{{ per.email }}</td>
                    <td>{{ per.salary }}</td>
                    <td>{{ per.dep.dep_name }}</td>
                </tr>
                {% empty %}
                <tr>
                    <td colspan="7">无相关记录!</td>
                </tr>
                {% endfor %}
                </tbody>
            </table>
            <nav aria-label="Page navigation">
                <ul class="pagination">
                    {{ page_nav|safe }}
                </ul>
            </nav>

        </div>
    </div>
</div>
<script src="{% static 'jquery-3.4.1.min.js' %}"></script>
<script src="{% static 'bootstrap/js/bootstrap.min.js'%}"></script>
</body>
</html>

页面把视图函数传过来的有关分页的变量以模板标签{{ page_nav|safe }}的形式放在页面上,这样在相应位置显示页码标签。

分页组件

如果每个视图函数遇到分页的情况都要写一遍分页代码,重复太多效率不高,因此有必要写一个分页组件,这样网页上的记录需要使用分页功能时,只需调用这个组件即可。

分页组件

在/test_orm/文件夹下新建一个文件夹utils,在其下新建一个paginater.py:

class Paginater():

    def __init__(self, url_address,cur_page_num, total_rows,  one_page_lines=10, page_maxtag=9):
        """
        url_address: 页码标签href的地址,也就是分页功能的网页URL
        cur_page_num: 当前页码数
        total_rows: 数据模型的记录总数
        one_page_lines: 每页显示多少条记录
        page_maxtag: 页面上显示页码标签的个数
        """
        self.url_address = url_address
        self.page_maxtag=page_maxtag

        """
        根据总记录数,计算出总的页数。
        通过divmod()函数取得商和余数,有余数时,总页数是商加上1
        同时判断当前页的数值是否大于总页数,如果大于总页数,设置当前页数等于最后一页的页数
        """
        total_page, remainder = divmod(total_rows, one_page_lines)
        if remainder:
            total_page += 1
        self.total_page = total_page
        try:
            # 参数page传递进来是字符型数值,因此需要转化为整数类型
            cur_page_num = int(cur_page_num)
            # 如果输入的页码数超过了最大的页码数,设置当前页数是最后一页的页数
            if cur_page_num > total_page:
                cur_page_num = total_page
            #避免出现当前页数为0
            if cur_page_num == 0:
                cur_page_num=1
        except Exception as e:
            # 当输入的页码不是正整数时, 设置当前页数是第一页的页数
            cur_page_num = 1
        self.cur_page_num = cur_page_num

        # 定义两个变量,指定表中取出的记录开始数,以及当前页记录结束的位置
        self.rows_start = (cur_page_num - 1) * one_page_lines
        self.rows_end = cur_page_num * one_page_lines
        # 如果页数小于每页设置的页码标签数,设置每页页码标签数为总页数
        if total_page < page_maxtag:
            page_maxtag = total_page
        # 把当前页码标签放在中间,前面放一半页码标签,后面放一半页码标签
        # 因此现把页面上放置的页码标签数除以2
        half_page_maxtag = page_maxtag // 2
        # 页面上页码开始
        page_start = cur_page_num - half_page_maxtag
        # 页面上页码结束
        page_end = cur_page_num + half_page_maxtag
        """
        如果当前页减一半,小于1,页面中页码标签设置从1开始,
        页面中页码标签等页面中页码标签数
        """
        if page_start <= 1:
            page_start = 1
            page_end = page_maxtag
        """
        如果当前页加 一半 比总页码数大,设置最后的页码标签值为总页数
        页面中页码标签起始等于总页数减掉页码标签数加1
        """
        if page_end >= total_page:
            page_end = total_page
            page_start = total_page - page_maxtag + 1
            if page_start <= 1:
                page_start = 1
        self.page_start=page_start
        self.page_end=page_end


    def html_page(self):
        # 初始一个列表变量,用来保存拼接分页的HTML代码
        html_page = []
        # 首页代码
        html_page.append('<li><a href="{}?page=1">首页</a></li>'.format(self.url_address))
        #  上一页页码标签的HTML代码,如果当前是第一页,设置上一页标签为非可用状态
        if self.cur_page_num <= 1:
            html_page.append('<li class="disabled"><a href="#"><span aria-hidden="true">&laquo;</span></a></li>'.format(
                self.cur_page_num - 1))
        else:
            # 上一页页码标签的HTML代码
            html_page.append('<li><a href="{}?page={}"><span aria-hidden="true">&laquo;</span></a></li>'.format(self.url_address, self.cur_page_num-1))
        # 依次取页码标签,注意切片函数用法
        for i in range(self.page_start, self.page_end + 1):
            # 如果是当前页就加一个active样式类
            if i == self.cur_page_num:
                tmp = '<li class="active"><a href="{0}?page={1}">{1}</a></li>'.format(self.url_address, i)
            else:
                tmp = '<li><a href="{0}?page={1}">{1}</a></li>'.format(self.url_address, i)
            html_page.append(tmp)

        # 下一页的页码标签的HTML代码
        # 判断,如果是最后一页,就没有下一页
        if self.cur_page_num >= self.total_page:
            html_page.append('<li class="disabled"><a href="#"><span aria-hidden="true">&raquo;</span></a></li>')
        else:
            html_page.append('<li><a href="{}?page={}"><span aria-hidden="true">&raquo;</span></a></li>'.format(self.url_address, self.cur_page_num+1))
        # 最后一页的页码标签的HTML代码
        html_page.append('<li><a href="{}?page={}">尾页</a></li>'.format(self.url_address, self.total_page))
        # 把HTML联结起来
        page_nav = "".join(html_page)
        return page_nav

    @property
    def data_start(self):
        return self.rows_start

    @property
    def data_end(self):
        return self.rows_end

说明:以上代码通过Paginater类对分页逻辑代码进行了封装,主要包括初始化、生成HTML代码片段、生成两个属性。

类中__init__函数接收URL、当前代码、记录总数、每页显示的记录数、每页页码标签数等参数,计算出总页数、当前页中记录从第几条开始并且到第几条结束,当前页面上的页码标签的开始数和结束数。

类中html_page函数通过__init__接收参数以及计算出的值生成分页相关的HTML代码。

类中data_start、data_end两个函数分别返回当前页面从哪条记录开始、哪条结束,通过@property装饰器变成类属性。

调用分页组件

视图函数person_pagenew调用了分页组件,实现了person表中记录的分页显示功能:

# 引入分页组件类
from utils.paginater import Paginater
def person_pagenew(request):
    # 从URL中取参数page,这个参数与pageinanter.py生成的代码片段有关
    cur_page_num = request.GET.get('page')
    if not cur_page_num:
        cur_page_num = '1'
    # 取得person中的记录总数
    total_count = models.person.objects.all().count()
    # 设定每一页显示多少条记录
    one_page_lines = 6
    # 页面上共展示多少页码标签
    page_maxtag = 9
    # 生成Paginater类的实例化对象
    page_obj = Paginater(url_address='/test_page/person_pagenew/',
                         cur_page_num=cur_page_num,total_rows=total_count,one_page_lines=one_page_lines,
                         page_maxtag=page_maxtag)
    # 对person表中的记录进行切片,取出本页的记录。
    page_list=models.person.objects.all()[page_obj.data_start:page_obj.page_end]
    return render(request,'test_page/list_person.html',{'person_list':page_list,'page_nav':page_obj.html_page()})
posted @ 2021-07-22 21:20  KKKyrie  阅读(81)  评论(0编辑  收藏  举报