­

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 等

彈窗中文支援不太好,手動修改樣式

調整一下樣式,Google瀏覽器看層級關係,然後手寫樣式覆蓋掉(這樣不會影響其他地方的)

<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>