Django-choices字段值对应关系(性别)-MTV与MVC科普-Ajax发json格式与文件格式数据-contentType格式-Ajax搭配sweetalert实现删除确认弹窗-自定义分页器

  • 2019 年 10 月 7 日
  • 筆記

目录

models 字段补充

choices 参数/字段(用的很多)

用户性别、用户学历、工作状态、婚否(可限范围内可以罗列出来的,都可用)

app01/models.py

from django.db import models      # Create your models here.  class User(models.Model):      username = models.CharField(max_length=32)      age = models.IntegerField()      choices = (          (1, '男'), (2, '女'), (3, '其他')      )      gender = models.IntegerField(choices=choices)        """      1 存choice里面罗列的数字与中文对应关系           print(user_obj.get_gender_display())              只要是choices字段 在获取数字对应的注释 固定语法              get_choices字段名_display()        2 存没有罗列迟来的数字          不会报错 还是展示数字      """      class Book(models.Model):      title = models.CharField(max_length=32)

同步到数据库,并加几条测试数据

在测试文件中试

只要是choice字段,在获取数字对应的注释,固定语法: get_choices字段名_display(),存没有罗列的数字,不会报错,还是展示数字

取值特点

  • 有对应关系就拿出对应关系
  • 如果没有对应关系,那就直接把原来的值拿出来

常见案例 CRM 中

MTV与MVC模型 科普

django 自称是 MTV 框架(本质其实也是 MVC)

  • M:models
  • T:templates
  • V:views

MVC

  • M:models
  • V:views
  • C:controller 控制器(urls)

Ajax

AJAX(Asynchronous Javascript And XML 异步的Javascript和XML)

特点:异步提交,局部刷新

例如:github 注册用户时,用户名是否存在的即时校验

发送 GET、POST 请求的几种常见方式

a 标签的 href 属性                   GET请求  直接在浏览器窗口输入 url 敲回车       GET请求  form 表单提交                       GET/POST  ajax 提交                           GET/POST

下面主要通过 ajax 来发送请求

ajax 这门技术是 Javascript 中的,但是原生的 Javascript 操作比较繁琐,我们这里为了方便使用,直接上手 jQuery 的 ajax

Ajax 最大的优点:在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感觉是在不知不觉中完成请求和相应过程)

用 Ajax 做一个小案例

页面上有三个 input 框,在前两个 input 框中输入数字,点击按钮发送 ajax 请求,在不刷新页面的情况下,在第三个框中自动填写两数之和

咱们这里是 jQuery 的 ajax,别忘了先引入 jQuery

准备工作

  • 新建一个项目,完成基本配置
    • 因为要用到 post 请求,所以先暂时把 settings 里的 csrf 中间件注释掉
  • 先写一个 url 匹配用户浏览器输入的地址
  • 再实现视图函数把页面发给浏览器,渲染成页面,返回一个页面给用户输入框内内容。

day57/settings.py

"""  Django settings for day57 project.    Generated by 'django-admin startproject' using Django 1.11.11.    For more information on this file, see  https://docs.djangoproject.com/en/1.11/topics/settings/    For the full list of settings and their values, see  https://docs.djangoproject.com/en/1.11/ref/settings/  """    import os    # Build paths inside the project like this: os.path.join(BASE_DIR, ...)  BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))      # Quick-start development settings - unsuitable for production  # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/    # SECURITY WARNING: keep the secret key used in production secret!  SECRET_KEY = 'p0d7eiqh64wgd#!!rso@!wv#4w(%-)6%hb6q^4ci=+h54h@syx'    # SECURITY WARNING: don't run with debug turned on in production!  DEBUG = True    ALLOWED_HOSTS = []      # Application definition    INSTALLED_APPS = [      'django.contrib.admin',      'django.contrib.auth',      'django.contrib.contenttypes',      'django.contrib.sessions',      'django.contrib.messages',      'django.contrib.staticfiles',      'app01.apps.App01Config',  # 1.检查 APP 是否已注册  ]    MIDDLEWARE = [      'django.middleware.security.SecurityMiddleware',      'django.contrib.sessions.middleware.SessionMiddleware',      'django.middleware.common.CommonMiddleware',      # 'django.middleware.csrf.CsrfViewMiddleware',  # 2.检查 csrf中间件 是否已注释掉      'django.contrib.auth.middleware.AuthenticationMiddleware',      'django.contrib.messages.middleware.MessageMiddleware',      'django.middleware.clickjacking.XFrameOptionsMiddleware',  ]    ROOT_URLCONF = 'day57.urls'    TEMPLATES = [      {          'BACKEND': 'django.template.backends.django.DjangoTemplates',          'DIRS': [os.path.join(BASE_DIR, 'templates')]          ,  # 3.检查 templates 文件夹是否已注册          'APP_DIRS': True,          'OPTIONS': {              'context_processors': [                  'django.template.context_processors.debug',                  'django.template.context_processors.request',                  'django.contrib.auth.context_processors.auth',                  'django.contrib.messages.context_processors.messages',              ],          },      },  ]    WSGI_APPLICATION = 'day57.wsgi.application'      # Database  # https://docs.djangoproject.com/en/1.11/ref/settings/#databases    DATABASES = {      'default': {          'ENGINE': 'django.db.backends.sqlite3',  # 4.检查 数据库配置信息(本文打算采用 django 自带的 sqlite3 进行测试,就不额外配置数据库了(__init__.py 文件里也就不配了))          'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),      }  }    LOGGING = {  # 5.添加数据库操作语句打印(一有数据库操作就会在 pycharm 控制台打印对应的 SQL 语句)      'version': 1,      'disable_existing_loggers': False,      'handlers': {          'console':{              'level':'DEBUG',              'class':'logging.StreamHandler',          },      },      'loggers': {          'django.db.backends': {              'handlers': ['console'],              'propagate': True,              'level':'DEBUG',          },      }  }    # Password validation  # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators    AUTH_PASSWORD_VALIDATORS = [      {          'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',      },      {          'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',      },      {          'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',      },      {          'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',      },  ]      # Internationalization  # https://docs.djangoproject.com/en/1.11/topics/i18n/    LANGUAGE_CODE = 'en-us'    TIME_ZONE = 'UTC'    USE_I18N = True    USE_L10N = True    USE_TZ = True      # Static files (CSS, JavaScript, Images)  # https://docs.djangoproject.com/en/1.11/howto/static-files/    STATIC_URL = '/static/'  STATICFILES_DIRS = [  # 6.配置静态资源路径(方便使用静态资源路径动态解析)      os.path.join(BASE_DIR, 'static')  ]

把 bootstrap 放到在项目根目录下 新建的 static 文件夹下

配置 url,本文采用路由分发(多多练习嘛)

day57/urls.py

"""day57 URL Configuration    The `urlpatterns` list routes URLs to views. For more information please see:      https://docs.djangoproject.com/en/1.11/topics/http/urls/  Examples:  Function views      1. Add an import:  from my_app import views      2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')  Class-based views      1. Add an import:  from other_app.views import Home      2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')  Including another URLconf      1. Import the include() function: from django.conf.urls import url, include      2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))  """  from django.conf.urls import url, include  # 下面用到了 include ,要自己导入这个模块  from django.contrib import admin    urlpatterns = [      url(r'^admin/', admin.site.urls),      url(r'^user/', include('app01.urls')),  # 写成字符串就会根据配置信息自动去查找,就不需要自己导模块过来了  ]

app01/urls.py

from django.conf.urls import url  from django.contrib import admin  from app01 import views    urlpatterns = [      url(r'^index/', views.index, name='user_index'),  # 匹配路由并取别名,方便后期反向解析 url  ]

app01/views.py

from django.shortcuts import render      # Create your views here.  def index(request):      return render(request, 'index.html')  # 用户访问 http://127.0.0.1:8000/user/index/ 直接给他返回 index.html 这个页面

templates/index.html

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>测试页</title>  {#    为了测试方便,这里就用网上的 jquery 了,用 CDN 的形式引入#}      <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>  {#    下面练习一下静态资源路径动态解析,并且 boostrap 放在本地会有语法提示,可以加快开发效率#}      {% load static %}      <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">      <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>  </head>  <body>  {#随便搭一个页面#}      <div class="container">          <div class="row">              <div class="text-center">                  <input type="text"> +                  <input type="text"> =                  <input type="text">                  <button class="btn btn-primary">计算</button>              </div>          </div>      </div>  </body>  </html>

浏览器访问测试是否基本工作准备完毕(成功)

动手用 Ajax 实现效果

思路分析 我们是输入信息,然后点击 计算按钮,由 ajax 向后端发起请求,后端拿到请求然后返回数据给前端,前端把数据填到结果框中 可以看出,我们的突破口是 计算按钮 的点击事件

app01/urls.py

from django.conf.urls import url  from django.contrib import admin  from app01 import views    urlpatterns = [      url(r'^index/', views.index, name='user_index'),      url(r'^compute/', views.compute, name='user_compute'),  # 用来处理用户的计算请求  ]

app01/views.py

from django.shortcuts import render, HttpResponse      # Create your views here.  def index(request):      return render(request, 'index.html')  # 用户访问 http://127.0.0.1:8000/user/index/ 直接给他返回 index.html 这个页面      from django.http import JsonResponse  # 下面发json数据要用到      def compute(request):      # 判读是否是 ajax 请求      if request.is_ajax():          # 定义一个空的字典,用来存放一会儿的返回信息          back_dic = {            }          # 前端 ajax采用的是 post 请求,所以这里这样获取数据          num1 = request.POST.get('num1')          num2 = request.POST.get('num2')          # 判断数据格式是否正确          if num1.isdigit() and num2.isdigit():              try:                  res = int(num1) + int(num2)                  back_dic['status'] = 200                  back_dic['res'] = res              except Exception as e:                  print(e)                  back_dic['status'] = 100                  back_dic['err'] = e          # 格式不正确返回错误信息          else:              back_dic['status'] = 100              back_dic['err'] = '请输入合法数字!'          # 用 JsonResponse 将字典打包成 json 格式数据返回给前端          #   json 格式的数据前端可以直接解析成 前端的自定义对象          return JsonResponse(back_dic)

templates/index.html

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>测试页</title>      {#    为了测试方便,这里就用网上的 jquery 了,用 CDN 的形式引入#}      <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>      {#    下面练习一下静态资源路径动态解析,并且 boostrap 放在本地会有语法提示,可以加快开发效率#}      {% load static %}      <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">      <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>  </head>  <body>  {#随便搭一个页面#}  <div class="container">      <div class="row">          <div class="text-center">              <input type="text" id="num1"> +              <input type="text" id="num2"> =              <input type="text" id="res">              <button class="btn btn-primary" id="button1">计算</button>          </div>      </div>  </div>  <script>      {#    1.为了能够获取到对应输入框和按钮以及输入框的值,我们需要先给他们加上 id #}      $('#button1').click(function (e) {  {# 2.给我们的 计算 按钮绑定点击事件,点击时用 ajax 发送请求,然后后端计算并返回数值,放在 id 为 res 的 input 里 #}          $.ajax({  {# 3.Jquery 的 ajax,需要下面几个参数,记得写上(Ajax 的括号内是一个大括号,然后再写的参数) #}              url: '{% url "user_compute" %}', {# 4.写 ajax 发送请求的地址,这里用的 url 反向解析 #}              type: 'post', {# 5.采用 post 请求发送数据 #}              data: {  {# 6.把要发送过去的数据打包 #}                  'num1': $('#num1').val(),  {# 7.获取到 id 为 num1 的标签的 value值 #}                  'num2': $('#num2').val()              },              success: function (data) {  {# 8.请求成功的回调函数 #}                  if (data.status == 200) {  {# 9.后端手动设置的码, 200 表示数据类型没问题 #}                      $('#res').val(data.res);  {# 数据没问题就打印出来 #}                  } else {  {# 10.码不是 200 表示数据类型有误等,弹窗提示错误信息 #}                      alert(data.err);                  }              }          })      })  </script>  </body>  </html>

请求页面查看效果

request.is_ajax() 用来判断当前请求方式是否是 ajax 请求(不管是 get 还是 post ,只要是 ajax 提交的,都能识别出来) 通过 ajax 提交的 post 请求,标签没有 name 属性也没关系,我们自己已经指定了键值对的键(name 在 form 表单中的主要作用)

contentType 前后端传输数据编码格式

前后端传输数据的编码格式(常见的)

  1. application/x-www-form-urlencoded
    • form 表单默认的编码格式
      • 数据格式:name=jason&pwd=123
    • django 后端针对 urlencoded 编码格式的数据会自动解析并放到 request.POST 中供用户获取(传文件的时候就拿到了文件名)
  2. multipart/form-data
    • django后端针对只要是符合 urlencoded 编码格式的数据会自动解析并放到 request.POST 中,只要你指定的编码是 formdata 就会自动解析并放到 request.FILES 中,供用户获取
  3. json

总结:前后端传输数据的时候,一定要保证数据格式和你的编码格式是一致的,不能乱来,不然不好拿数据

ajax 提交数据

ajax 默认数据提交方式也是 urlencoded

不符合格式就不要了,前后端编码和数据格式一致就不会出现这个问题

ajax发 json 格式数据

django 后端针对 json 格式的数据,并不会自动解析放到 request.POST 或 request.FILES 里面,它并不会解析 json 格式数据,而是将它原封不动地放在 request.body 里了

$('#b1').on('click',function () {      // 点击按钮 朝后端发送post请求      $.ajax({          url: '',  // 控制发送给谁 不写就是朝当前地址提交          type: 'post',  // 发送方式是post请求          data: JSON.stringify({'username': 'jason', 'password': 123}),  // 发送的数据          contentType: 'application/json',  // 告诉后端你这次的数据是json格式            success: function (data) {  // data形参用来接收异步提交的结果              // 将后端计算好的结果 通过DOM操作 渲染到第三个input矿中              $('#i3').val(data)          }      })  })

后端需要手动去 request.body 中获取 json 格式数据,自己处理

from django.shortcuts import render,HttpResponse  from app01 import models  # Create your views here.  def index(request):      # 验证前后端传输数据的编码格式      if request.method == 'POST':          print(request.body)          # 后端 需要手动去request.body中获取json格式数据  自己处理          json_str = request.body          import json          s = json_str.decode('utf-8')          ral_d = json.loads(s)          print(ral_d, type(ral_d))          return HttpResponse('OK')      return render(request, 'index.html')

ajax 传文件

如何获取文件标签所存储的文件对象?

  1. 先用 jQuery 查找到存储文件的 input 标签
  2. 将 jQuery 对象转成原生 js 对象
  3. 利用 原生 js 对象 的方法 .files[0] 获取到标签内部存储的文件对象
  4. 一定要指定两个参数(processData、contentType)都为 false
 // ajax传输文件  $('#b1').on('click',function () {      // ajax 传输文件 建议使用内置对象 formdata      var formData = new FormData();  // 既可以传普通的键值对 也可以传文件      // 添加普通键值(后续可以改成 for 循环填充)      formData.append('username','jason');      formData.append('password','123');      formData.append('my_file',$('#d1')[0].files[0]);      $.ajax({          url:'',  // 控制发送给谁 不写就是朝当前地址提交          type:'post',  // 发送方式是post请求          data:formData, // 发送的数据            // ajax发送文件需要指定两个额外的参数          processData:false,  // 告诉前端不要处理数据          contentType:false,  // 不适用任何编码  因为formdata对象自身自带编码 django后端也能够识别formdata对象            success:function (data) {  // data形参用来接收异步提交的结果              {# alert(data) #}              // 将后端计算好的结果 通过DOM操作 渲染到第三个input矿中              $('#i3').val(data)          }        })  })

序列化组件

前后端分离的知识点

模拟前后端分离,给前端传递一个 json 格式的大字典

from django.core import serializers  # django自带的一个小型的序列化工具  from app01 import models  import json      def reg(request):      user_list = models.User.objects.all()      # 将所有的数据都组织成一个符合 json 格式的一个个的字典      # user_l = []      # for user_obj in user_list:      #     user_l.append(      #         json.dumps({      #             'username': user_obj.username,      #             'age': user_obj.age,      #             'gender': user_obj.get_gender_display()      #         })      #     )      res = serializers.serialize('json', user_list)  # 指定序列化格式为 json      return render(request, 'index.html', locals())

前段收到的 json 格式数据

[{      "model": "app01.user",      "pk": 1,      "fields": {          "username": "jason",          "age": 18,          "gender": 1      }  }, {      "model": "app01.user",      "pk": 2,      "fields": {          "username": "tank",          "age": 24,          "gender": 3      }  }, {      "model": "app01.user",      "pk": 3,      "fields": {          "username": "egon",          "age": 73,          "gender": 2      }  }, {      "model": "app01.user",      "pk": 7,      "fields": {          "username": "kevin",          "age": 29,          "gender": 4      }  }]

如果格式比较乱都成一行了,可以找个工具网站帮忙解析一下(在线 json 校验格式化

目前小型的 序列化模块 serializers

还有更好的,rest_framework 。。。后面会涉及到

from app01 import models  from django.core import serializers  # django 自带的一个小型的序列化工具    def reg(request):      user_list = models.User.objects.all()      res = serializers.serialize('json', user_list)      return render(request, 'index.html', locals())

利用 sweetalert 搭建页面(弹窗)

  • 先确保静态资源文件配置 settings.py
  • 把 sweetalert 下过来,放到 static 下
  • 动态解析页面上引入对应的内容:jq、bootstrap(css/js)、sweetalert(css/js)(我们用的依赖于bootstrap)

下面的按钮还没写好,还要绑定 id 等

弹窗中文支持不太好,手动修改样式

调整一下样式,谷歌浏览器看层级关系,然后手写样式覆盖掉(这样不会影响其他地方的)

<style>      div.sweet-alert h2{          padding-top: 10px;      }  </style>

sweetalert 官网 找样式过来,放在 script 里,给某个按钮绑定事件,触发它

cancelButtonText 修改取消文本(自己加的)

给按钮添加自定义属性,绑定 user_id,弹窗确认删除那里写 ajax 获取到 user_id 发 ajax 过去

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Title</title>      <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>      {% load static %}      <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">      <link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">      <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>      <script src="{% static 'dist/sweetalert.min.js' %}"></script>        <style>          div.sweet-alert h2{              padding-top: 10px;          }      </style>  </head>  <body>  <div class="container">      <div class="row">          <div class="col-md-8 col-md-offset-2">              <h2 class="text-center">数据展示</h2>              <table class="table-striped table table-hover table-bordered">                  <thead>                      <tr>                          <th>序号</th>                          <th>主键</th>                          <th>用户名</th>                          <th>年龄</th>                          <th>性别</th>                          <th>操作</th>                      </tr>                  </thead>                  <tbody>                      {% for user in user_list %}                          <tr>                              <td>{{ forloop.counter }}</td>                              <td>{{ user.pk }}</td>                              <td>{{ user.username }}</td>                              <td>{{ user.age }}</td>                              <td>{{ user.get_gender_display }}</td>                              <td>                                  <button class="btn btn-primary btn-sm">编辑</button>                                  <button class="btn btn-danger btn-sm del" user_id="{{ user.pk }}">删除</button>                              </td>                          </tr>                      {% endfor %}                  </tbody>              </table>          </div>      </div>  </div>  <script>      $('.del').click(function () {          // 获取当前标签对象          var $btnEle = $(this);            swal({            title: "你确定要删吗?",            text: "你要是删了,你就准备跑路吧!",            type: "warning",            showCancelButton: true,            confirmButtonClass: "btn-danger",            confirmButtonText: "是的,老子就要删!",            cancelButtonText: "算了,算了!",            closeOnConfirm: false,            showLoaderOnConfirm: true          },          function(){              // 朝后端发送ajax请求              $.ajax({                  url:'',                  type:'post',                  data:{'delete_id':$btnEle.attr('user_id')},                  success:function (data) {  // 后端发字典过来 前端不需要你手动转 会自动帮你转换成js自定义对象                      if (data.code == 100){                          {#window.location.href = '';  // 不写就是条到当前页面#}                          // 通过DOM操作 实时改变页面                          // 将被点击的删除按钮所在的那一行直接从DOM树中删掉                          $btnEle.parent().parent().remove();                          swal("删掉了!", "赶紧回去收拾行李吧,准备跑路!", "success");                      }else{                          swal('发生了未知的错误','估计是有bug了','info')                      }                  }              });            });      })  </script>    </body>  </html>

后端把字典用JsonResponse 发送,前端可以拿到并自动解析成自定义对象,可以直接用 点语法操作它的属性

from django.http import JsonResponse  import time  def userlist(request):      if request.is_ajax():          time.sleep(3)          """          一般情况下 针对ajax请求 后端通常都是返回一个字典          """          back_dic = {'code':100,'msg':''}          # 直接获取用户想删除的数据的id值          delete_id = request.POST.get('delete_id')          # 直接利用queryset方法 批量删除          models.User.objects.filter(pk=delete_id).delete()          # 要给前端ajax返回一个消息(字典)          back_dic['msg'] = '真的删除了!'          return JsonResponse(back_dic)          user_list = models.User.objects.all()      return render(request,'userlist.html',locals())

页面数据没刷新的问题

直接刷新是最偷懒的办法

这个方法不太好(弹窗的第二段动画还没放完它就刷新页面了)

删除整行

获取父标签(整行),然后通过 DOM 操作 把它从 DOM 树中移除掉 $btnEle.parent().parent().remove()

序号没有连续先不管,不是这里的重点(–> 1,3,4,5)

自定义分页器

批量插入测试数据 bulk_create

效果和一条一条插入,快的不是一点点(一条条插入1000条,要一分多钟,批量秒插)

l = []    for i in range(10000):      l.append(models.Book(title=f'书籍{i}'))  models.Book.objects.bulk_create()  # 批量插入数据

分页

有这么几个关键参数:每页多少条、起始页、开始条数、结束条数

推导它们的关系规律

"""  per_page_num = 10    current_page                start_page                  end_page      1                           0                           10      2                           10                          20      3                           20                          30      4                           30                          40    per_page_num = 5    current_page                start_page                  end_page      1                           0                           5      2                           5                           10      3                           10                          15      4                           15                          20    start_page = (current_page - 1) * per_page_num  end_page =  current_page* per_page_num      10000  10    1000      10001  10    1001    """

可以用 内置函数 divmod(101, 10) –> 10 1 来计算页数

在后端用循环来渲染 html 标签(前端做不了)

页码的边界判断

使用最终版的实现分页器

一般第三方通用的文件都会新建一个 utils 文件夹,然后把 python 代码 放到里面

后端

创建文件夹与文件,把分页代码放进去,导入过来,使用

app01/utils/mypage.py 写好的分页类,直接拿来用就好了

class Pagination(object):      def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):          """          封装分页相关数据          :param current_page: 当前页          :param all_count:    数据库中的数据总条数          :param per_page_num: 每页显示的数据条数          :param pager_count:  最多显示的页码个数            用法:          queryset = model.objects.all()          page_obj = Pagination(current_page,all_count)          page_data = queryset[page_obj.start:page_obj.end]          获取数据用page_data而不再使用原始的queryset          获取前端分页样式用page_obj.page_html          """          try:              current_page = int(current_page)          except Exception as e:              current_page = 1            if current_page < 1:              current_page = 1            self.current_page = current_page            self.all_count = all_count          self.per_page_num = per_page_num            # 总页码          all_pager, tmp = divmod(all_count, per_page_num)          if tmp:              all_pager += 1          self.all_pager = all_pager            self.pager_count = pager_count          self.pager_count_half = int((pager_count - 1) / 2)        @property      def start(self):          return (self.current_page - 1) * self.per_page_num        @property      def end(self):          return self.current_page * self.per_page_num        def page_html(self):          # 如果总页码 < 11个:          if self.all_pager <= self.pager_count:              pager_start = 1              pager_end = self.all_pager + 1          # 总页码  > 11          else:              # 当前页如果<=页面上最多显示11/2个页码              if self.current_page <= self.pager_count_half:                  pager_start = 1                  pager_end = self.pager_count + 1                # 当前页大于5              else:                  # 页码翻到最后                  if (self.current_page + self.pager_count_half) > self.all_pager:                      pager_end = self.all_pager + 1                      pager_start = self.all_pager - self.pager_count + 1                  else:                      pager_start = self.current_page - self.pager_count_half                      pager_end = self.current_page + self.pager_count_half + 1            page_html_list = []          # 添加前面的nav和ul标签          page_html_list.append('''                      <nav aria-label='Page navigation>'                      <ul class='pagination'>                  ''')          first_page = '<li><a href="?page=%s">首页</a></li>' % (1)          page_html_list.append(first_page)            if self.current_page <= 1:              prev_page = '<li class="disabled"><a href="#">上一页</a></li>'          else:              prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)            page_html_list.append(prev_page)            for i in range(pager_start, pager_end):              if i == self.current_page:                  temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)              else:                  temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)              page_html_list.append(temp)            if self.current_page >= self.all_pager:              next_page = '<li class="disabled"><a href="#">下一页</a></li>'          else:              next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)          page_html_list.append(next_page)            last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)          page_html_list.append(last_page)          # 尾部添加标签          page_html_list.append('''                                             </nav>                                             </ul>                                         ''')          return ''.join(page_html_list)

app01/views.py 中使用

# ...其他代码  from app01.utils.mypage import Pagination  def book(request):      # 使用封装好的自定义分页器      book_list = models.Book.objects.all()      current_page = request.GET.get("page", 1)      all_count = book_list.count()      page_obj = Pagination(current_page=current_page, all_count=all_count, per_page_num=10)      page_queryset = book_list[page_obj.start:page_obj.end]        return render(request, 'booklist.html', locals())  # ...其他代码

templates/booklist.html 页面上

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Title</title>      <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>      {% load static %}      <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">      <link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">      <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>      <script src="{% static 'dist/sweetalert.min.js' %}"></script>  </head>  <body>  <div class="container">      <div class="row">          <div class="col-md-8 col-md-offset-2">              {% for book in page_queryset %}                  <p>{{ book.title }}</p>              {% endfor %}              {{ page_obj.page_html|safe }}  {# 支持转义,让 html 格式的字符串可以渲染出来 #}          </div>      </div>  </div>  </body>  </html>