Django-手擼簡易web框架-實現動態網頁-wsgiref初識-jinja2初識-python主流web框架對比-00

  • 2019 年 10 月 3 日
  • 筆記

[TOC]

自己動手實現一個簡易版本的web框架

在了解python的三大web框架之前,我們先自己動手實現一個。

備註:

這部分重在掌握實現思路,代碼不是重點

代碼中也有許多細節並未考慮,重在實現思路

手擼一個web服務端

我們一般是使用瀏覽器當做客戶端,然後基於HTTP協議自己寫服務端代碼作為服務端

先自行去回顧一下HTTP協議這一塊兒的知識

import socket  server = socket.socket()  # 基於socket通信(TCP)  server.bind(('127.0.0.1', 8080))  server.listen(5)  while True:      conn, addr = server.accept()      data = conn.recv(2048)  # 接收請求      print(str(data, encoding='utf-8'))      conn.send(b'HTTP/1.1 200 OKrnrn')  # 依據HTTP協議,發送響應給客戶端(瀏覽器),這裡是響應首行 + 響應頭 + 空行      # response = bytes('<h3>這是響應內容</h3>', encoding='GBK')      response = '<h3>這是響應內容</h3>'.encode('GBK')  # 我電腦上是GBK編碼,所以使用GBK編碼將字符串轉成二進制      conn.send(response)  #  繼續發送響應體      conn.close()  # 斷開連接(無狀態、無連接)    # 瀏覽器發過來的數據如下  '''  GET / HTTP/1.1  Host: 127.0.0.1:8080  Connection: keep-alive  Upgrade-Insecure-Requests: 1  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3  Accept-Encoding: gzip, deflate, br  Accept-Language: zh-CN,zh;q=0.9  Cookie: _qddaz=QD.w3c3g1.j2bfa7.jvp70drt; csrftoken=kJHVGICQOglLxJNiui0o0UyxNtR3cXbJPXqaUFs5FoxeezuskRO7jlQE0JNwYXJs      GET /favicon.ico HTTP/1.1  Host: 127.0.0.1:8080  Connection: keep-alive  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36  Accept: image/webp,image/apng,image/*,*/*;q=0.8  Referer: http://127.0.0.1:8080/  Accept-Encoding: gzip, deflate, br  Accept-Language: zh-CN,zh;q=0.9  Cookie: _qddaz=QD.w3c3g1.j2bfa7.jvp70drt; csrftoken=kJHVGICQOglLxJNiui0o0UyxNtR3cXbJPXqaUFs5FoxeezuskRO7jlQE0JNwYXJs  '''  

然後右鍵運行,在瀏覽器訪問 127.0.0.1:8080 即可看到響應數據

關於啟動服務器與頁面請求(在我處理的時候,頁面網絡請求會經常處於 pending狀態,不是很清楚原因,一般這個情況下,直接重啟一下服務器即可)

根據請求 url 做不同的響應處理

上面的代碼已經實現了基本請求響應,那如何根據不同的請求作出不同的響應呢?

我們輸入不同的url,看看服務器端會返回什麼

分析請求

瀏覽器訪問 http://127.0.0.1:8080/index  GET /index HTTP/1.1  Host: 127.0.0.1:8080  Connection: keep-alive  Cache-Control: max-age=0  Upgrade-Insecure-Requests: 1  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3  Accept-Encoding: gzip, deflate, br  Accept-Language: zh-CN,zh;q=0.9  Cookie: _qddaz=QD.w3c3g1.j2bfa7.jvp70drt; csrftoken=kJHVGICQOglLxJNiui0o0UyxNtR3cXbJPXqaUFs5FoxeezuskRO7jlQE0JNwYXJs      瀏覽器訪問 http://127.0.0.1:8080/home  GET /home HTTP/1.1  Host: 127.0.0.1:8080  Connection: keep-alive  Cache-Control: max-age=0  Upgrade-Insecure-Requests: 1  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3  Accept-Encoding: gzip, deflate, br  Accept-Language: zh-CN,zh;q=0.9  Cookie: _qddaz=QD.w3c3g1.j2bfa7.jvp70drt; csrftoken=kJHVGICQOglLxJNiui0o0UyxNtR3cXbJPXqaUFs5FoxeezuskRO7jlQE0JNwYXJs  

原來請求首行的 GET 後面跟的就是請求我們想要信息(/index 首頁、/home 家)

這些信息也是我們接收到的(data = conn.recv(2048) print(str(data, encoding='utf-8'))),那可不可以取出來,根據值的不同作不同處理呢?

處理請求,獲取 url

data = '''GET /home HTTP/1.1  Host: 127.0.0.1:8080  Connection: keep-alive  Cache-Control: max-age=0  Upgrade-Insecure-Requests: 1  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3  Accept-Encoding: gzip, deflate, br  Accept-Language: zh-CN,zh;q=0.9  Cookie: _qddaz=QD.w3c3g1.j2bfa7.jvp70drt; csrftoken=kJHVGICQOglLxJNiui0o0UyxNtR3cXbJPXqaUFs5FoxeezuskRO7jlQE0JNwYXJs'''  print(data.split('n')[0].split(' ')[1])  # ... ---> GET /home HTTP/1.1 --> ['GET', '/home', 'HTTP/1.1']  --> /home  # /home  

依據上述切割規則,我們來對不同的請求作出不同的響應

import socket  server = socket.socket()  server.bind(('127.0.0.1', 8080))  server.listen(5)  while True:      conn, addr = server.accept()      data = conn.recv(2048).decode('utf-8')      data = data.split('n')[0].split(' ')[1]      print(data)      conn.send(b'HTTP/1.1 200 OKrnrn')      if data == '/index':          response = '<h3>這裡是 index...</h3>'.encode('GBK')      elif data == '/home':          response = '<h3>這裡是 home...</h3>'.encode('GBK')      else:          response = '<h3>404 NOT FOUND...n找不到您要找的資源...</h3>'.encode('GBK')      conn.send(response)      conn.close()    # --- 瀏覽器請求 http://127.0.0.1:8080/index 的打印信息  # /index  # /favicon.ico  # --- 瀏覽器請求 http://127.0.0.1:8080/home 的打印信息  # /home  # /favicon.ico  # --- 瀏覽器請求 http://127.0.0.1:8080/de2332f 的打印信息  # /de2332f  # /favicon.ico    

頁面成功顯示不同的信息

http://127.0.0.1:8080/index

http://127.0.0.1:8080/home

http://127.0.0.1:8080/de2332f

404頁面也應該算作設計網站的一部分,可以給人不一樣的感覺

基於wsgiref模塊實現服務端

前面處理 scoket 和 http 的那堆代碼通常是不變的,且與業務邏輯沒什麼關係,如果每個項目都要寫一遍,那豈不是很麻煩?那封裝成模塊嘛~

不過這個操作已經有人幫我們做了,並且封裝的更加強大,就是 wsgiref 模塊

用wsgiref 模塊的做的兩件事

  1. 在請求來的時候,自動解析 HTTP 數據,並打包成一個字典,便於對請求發過來的數據進行操作
  2. 發響應之前,自動幫忙把數據打包成符合 HTTP 協議的格式(響應數據格式,不需要再手動寫 conn.send(b'HTTP/1.1 200 OKrnrn') 了),返回給服務端
from wsgiref.simple_server import make_server  # 導模塊      def run(env, response):      """      先不管這裡的 env 和 response 什麼個情況        env:是請求相關的數據,wsgiref幫我們把請求包裝成了一個大字典,方便取值      response:是響應相關的數據      """      response('200 OK', [])      print(env)      current_path = env.get('PATH_INFO')      print(current_path)        if current_path == '/index':          return ['hello, there is index...'.encode('utf-8')]      elif current_path == '/login':          return ['hello, there is login...'.encode('utf-8')]      else:          return ['sorry... that pages you want is not found...'.encode('utf-8')]      if __name__ == '__main__':      # 實時監測 127.0.0.1:8080 地址,一旦有客戶端連接,會自動加括號調用 run 方法      server = make_server('127.0.0.1', 8080, run)      server.serve_forever()  # 啟動服務器      # /index  # ---> env 的數據(手動刪減了一些),可以看到其中有個 PATH_INFO 是我們要的東西(還有瀏覽器版本啊,USER-AGENT啊,客戶端系統環境變量啊之類的信息)  '''{'ALLUSERSPROFILE': 'C:\ProgramData', ...省略部分... , 'COMSPEC': 'C:\Windows\system32\cmd.exe', 'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 60 Stepping 3, GenuineIntel', 'PROCESSOR_LEVEL': '6', 'PROCESSOR_REVISION': '3c03', 'PYTHONIOENCODING': 'UTF-8',  'SESSIONNAME': 'Console', 'SYSTEMDRIVE': 'C:', 'SERVER_PORT': '8080', 'REMOTE_HOST': '', 'CONTENT_LENGTH': '', 'SCRIPT_NAME': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'SERVER_SOFTWARE': 'WSGIServer/0.2', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/index', 'QUERY_STRING': '', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'text/plain', 'HTTP_HOST': '127.0.0.1:8080', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3', csrftoken=kJHVGICQOglLxJNiui0o0UyxNtR3cXbJPXqaUFs5FoxeezuskRO7jlQE0JNwYXJs', mode='w' encoding='UTF-8'>, 'wsgi.version': (1, 0), }'''  

伏筆

拆分服務端代碼

服務端代碼、路由配置、視圖函數,照目前的寫法全都冗在一塊兒,後期功能擴展時,這個文件會變得很長,不方便維護,所以選擇把他拆分開來

就是將服務端代碼拆分成如下三部分:

  • server.py 放服務端代碼

  • urls.py 放路由與視圖函數對應關係

  • views.py 放視圖函數/類(處理業務邏輯)

views.py

def index(env):      return 'index'      def login(env):      return 'login'  

urls.py

from views import *    urls = [      ('/index', index),      ('/login', login),  ]    

server.py

from wsgiref.simple_server import make_server  # 導模塊  from urls import urls  # 引入 urls.py 里的 urls列表(命名的不是很規範)      def run(env, response):      response('200 OK', [])      current_path = env.get('PATH_INFO')        func = None      for url in urls:          if current_path == url[0]:              func = url[1]              break        if func:          res = func(env)      else:          res = '404 Not Found.'      return [res.encode('utf-8')]  # 注意這裡返回的是一個列表(可迭代對象才行),wsgiref 模塊規定的,可能還有其他的用途吧      if __name__ == '__main__':      server = make_server('127.0.0.1', 8080, run)      server.serve_forever()    

支持新的請求地址(添加新頁面/新功能)

經過上面的拆分後,後續想要支持其他 url,只需要在 urls.py 中添加一條對應關係,在 views.py 中把該函數實現,重啟服務器即可訪問

以支持 http://127.0.0.1:8080/new_url 訪問為例

urls.py

from views import *    urls = [      ('/index', index),      ('/login', login),      ('/new_url', new_url),  ]    

views.py

def index(env):      return 'index'      def login(env):      return 'login'      def new_url(env):      # 這裡可以寫一堆邏輯代碼      return 'new_url'  

重啟服務器,打開瀏覽器即可訪問 http://127.0.0.1:8080/new_url

擴展性高了很多,且邏輯更清晰了,更不容易弄錯(框架的好處提現,也是為什麼脫離了框架不會寫的原因,這塊代碼寫的太少,不常用到,沒了框架又寫不出來)

動態靜態網頁–拆分模板文件

前面寫了那麼多,都只是一直在返回純文本信息,而我們一般請求頁面返回的都是瀏覽器渲染好的華麗的頁面,那要怎麼放回華麗的頁面呢?

頁面嘛,就是 HTML + CSS + JS 渲染出來的,所以我們也可以把 HTML文件當成數據放在響應體里直接返回回去

新建一個功能的步驟還是複習一下

  • 在 urls.py 裏面加一條路由與視圖函數的對應關係
  • 在 views.py 裏面加上那個視圖函數,並寫好內部邏輯代碼
  • 重啟服務器,瀏覽器打開頁面訪問

返回靜態頁面–案例

這裡咱們就接着上面的 new_url 寫,用他來返回 一個網頁

新建一個 templates 文件夾,專門用來放 HTML 文件

templates/new_url.html

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Title</title>  </head>  <body>      <h1>New URL</h1>      <h5>Wellcome!</h5>  </body>  </html>  

views.py

def index(env):      return 'index'      def login(env):      return 'login'      def new_url(env):      # 讀取並把 new_url 文件返回給客戶端(瀏覽器)      with open(r'templates/new_url.html', 'rb') as f:  		html_data = f.read()      return html_data.decode('utf-8')  # 因為 run 函數那裡做了 encode, 而二進制數據沒有 encode這個方法,所以這裡先解碼一下,然後那邊再編碼一下  

重啟服務器,使用瀏覽器訪問

上面提到了靜態頁面,那什麼是靜態頁面?什麼又是動態頁面呢?

  • **靜態網頁:**純html網頁,數據是寫死的,所有同url的請求拿到的數據都是一樣的

  • **動態網頁:**後端數據拼接,數據不是寫死的,是動態拼接的,比如:

    ​ 後端實時獲取當前時間「傳遞」(塞)給前端頁面展示

    ​ 後端從數據庫獲取數據「傳遞」給前端頁面展示

實現返回時間–插值思路(動態頁面)

要怎麼在 html 里插入時間呢?

往 html 里的插入?那替換好像也可以達到效果啊?

html_data = f.read() ? 好像 html 被讀出出來了,而且還是二進制的,二進制可以 decode 變成字符串,字符串有 replace方法可以替換字符串,那我隨便在網頁里寫點內容,然後替換成時間?

先把基礎歩鄹做好

templates/get_time.html 編寫展示頁面

put_times_here 用來做佔位符,一會兒給他替換成時間

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Title</title>  </head>  <body>      <h1>北京時間:</h1>      <h1>put_time_here</h1>  </body>  </html>  

urls.py 路由與視圖函數對應關係

from views import *    urls = [      ('/index', index),      ('/login', login),      ('/new_url', new_url),      ('/get_time', get_time),  ]    

views.py 實現視圖函數

def index(env):      return 'index'      def login(env):      return 'login'      def new_url(env):      # 讀取並把 new_url 文件返回給客戶端(瀏覽器)      with open(r'templates/new_url.html', 'rb') as f:  		html_data = f.read()      return html_data      def get_time(env):      # 讀取並把 get_time 文件返回給客戶端(瀏覽器)      with open(r'templates/get_time.html', 'rb') as f:          html_data = f.read().decode('utf-8')      import time      html_data = html_data.replace('put_time_here', time.strftime("%Y-%m-%d %X"))      return html_data  

重啟服務器並打開瀏覽器訪問 http://127.0.0.1:8080/get_time

關鍵思路:相當於佔位符,字符串替換,後期把前端要替換的字符的格式統一規定下,方便閱讀與統一處理,這其實也就是目前的模版語法的雛形

我們只需要把處理好的字符串(HTML格式的)返回給瀏覽器,待瀏覽器渲染即可有頁面效果

利用 jinja2 模塊實現動態頁面

jinja2模塊有着一套 模板語法,可以幫我更方便地在 html 寫代碼(就想寫後台代碼一樣),讓前端也能夠使用後端的一些語法操作後端傳入的數據

安裝 jinja2

jinja2 並不是 python 解釋器自帶的,所以需要我們自己安裝

​ 由於 flask 框架是依賴於 jinja2 的,所下載 flask 框架也會自帶把 jinja2 模塊裝上

命令行執行,pip3 install jinja2圖形化操作安裝(參考 Django 的安裝方法)

初步使用

這裡只是知道有模板語法這麼一個東西可以讓我們很方便的往 html 寫一些變量一樣的東西,並不會講 jinja2 的語法,後續會有的

案例–展示字典信息

urls.py

from views import *    urls = [      ('/index', index),      ('/login', login),      ('/new_url', new_url),      ('/get_time', get_time),      ('/show_dic', show_dic),  ]    

views.py

def index(env):      return 'index'      def login(env):      return 'login'      def new_url(env):      # 讀取並把 new_url 文件返回給客戶端(瀏覽器)      with open(r'templates/new_url.html', 'rb') as f:  		html_data = f.read()      return html_data      def get_time(env):      # 讀取並把 get_time 文件返回給客戶端(瀏覽器)      with open(r'templates/get_time.html', 'rb') as f:          html_data = f.read().decode('utf-8')      import time      html_data = html_data.replace('put_time_here', time.strftime("%Y-%m-%d %X"))      return html_data      def show_dic(env):      user = {          "username": "jason",          "age": 18,      }      with open(r'templates/show_dic.html', 'rb') as f:          html_data = f.read()        # 使用 jinja2 的模板語法來將數據渲染到頁面上(替換佔位符)      from jinja2 import Template      tmp = Template(html_data)      res = tmp.render(dic=user)  # 將字典 user 傳遞給前端頁面,前端頁面通過變量名 dic 就能夠獲取到該字典      return res  

templates/show_dic.html 寫頁面

jinja2 給字典擴展了點語法支持({{ dic.username }}

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Title</title>  </head>  <body>      <h1>Nice to meet you~ i'm {{ dic.username }} , and i'm {{ dic.age }} years old.</h1>      <p>username: {{ dic['username']}}</p>      <p>age: {{ dic.get('age')}}</p>  </body>  </html>  

重啟服務器並打開瀏覽器訪問 http://127.0.0.1:8080/show_dic

為什麼說動態?

如果你改變了字典里的值,那麼請求這個頁面,顯示的數據也會跟着改變(注意這個字典一般都是其他地方獲取過來的)

  模板語法(貼近python語法): 前端也能夠使用後端的一些語法操作後端傳入的數據  	{{data.password}}  # jinja2 多給字典做了 點語法支持      ... 其他的語法,寫法        for 循環      {%for user_dict in user_list%}      	<tr>          	<td>{{user_dict.id}}</td>          	<td>{{user_dict.name}}</td>          	<td>{{user_dict.password}}</td>          </tr>      {%endfor%}    

進階案例–渲染數據庫數據到頁面

思路

pymsql 從數據庫取數據(指定成 列表套字典 的格式(DictCursor))  後台 python 代碼處理數據  交由 jinja2 模塊語法渲染到 html 頁面上    數據條數不定怎麼辦?      有多少條記錄就顯示多少條唄...循環?      表格格式先寫好,然後循環渲染數據到標籤上(特定語法表示循環)  

數據準備

創建數據庫 django_test_db,然後執行如下 SQL 命令

/*   Navicat MySQL Data Transfer     Source Server         : localhost-E   Source Server Type    : MySQL   Source Server Version : 50645   Source Host           : localhost:3306   Source Schema         : django_test_db     Target Server Type    : MySQL   Target Server Version : 50645   File Encoding         : 65001     Date: 15/09/2019 00:41:09  */    SET NAMES utf8mb4;  SET FOREIGN_KEY_CHECKS = 0;    -- ----------------------------  -- Table structure for user_info  -- ----------------------------  DROP TABLE IF EXISTS `user_info`;  CREATE TABLE `user_info`  (    `id` int(11) NOT NULL AUTO_INCREMENT,    `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,    `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,    PRIMARY KEY (`id`) USING BTREE  ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;    -- ----------------------------  -- Records of user_info  -- ----------------------------  INSERT INTO `user_info` VALUES (1, 'jason', '123');  INSERT INTO `user_info` VALUES (2, 'tank', '123');  INSERT INTO `user_info` VALUES (3, 'jerry', '123');  INSERT INTO `user_info` VALUES (4, 'egon', '456');    SET FOREIGN_KEY_CHECKS = 1;    

配路由與視圖函數

urls.py

from views import *    urls = [      ('/index', index),      ('/login', login),      ('/new_url', new_url),      ('/get_time', get_time),      ('/show_dic', show_dic),      ('/get_users', get_users),  ]    

views.py

def index(env):      return 'index'      def login(env):      return 'login'      def new_url(env):      # 讀取並把 new_url 文件返回給客戶端(瀏覽器)      with open(r'templates/new_url.html', 'rb') as f:  		html_data = f.read()      return html_data      def get_time(env):      # 讀取並把 get_time 文件返回給客戶端(瀏覽器)      with open(r'templates/get_time.html', 'rb') as f:          html_data = f.read().decode('utf-8')      import time      html_data = html_data.replace('put_time_here', time.strftime("%Y-%m-%d %X"))      return html_data      def show_dic(env):      user = {          "username": "jason",          "age": 18,      }      with open(r'templates/show_dic.html', 'rb') as f:          html_data = f.read()        # 使用 jinja2 的模板語法來將數據渲染到頁面上(替換佔位符)      from jinja2 import Template      tmp = Template(html_data)      res = tmp.render(dic=user)  # 將字典 user 傳遞給前端頁面,前端頁面通過變量名 dic 就能夠獲取到該字典      return res      # 先寫個空函數在這裡佔位置,去把 pymysql 查數據的寫了再過來完善  def get_users(env):      # 從數據庫取到數據      import op_mysql      user_list = op_mysql.get_users()        with open(r'templates/get_users.html', 'r', encoding='utf-8') as f:          html_data = f.read()        from jinja2 import Template  # 其實這個引入應該放在頁面最上方去的,但為了漸進式演示代碼推進過程,就放在這裡了      tmp = Template(html_data)      res = tmp.render(user_list=user_list)      return res  

**op_mysql.py **如果你的配置不一樣要自己改過來

import pymysql      def get_cursor():      server = pymysql.connect(          # 根據自己電腦上 mysql 的情況配置這一塊的內容          host='127.0.0.1',          port=3306,          user='root',          password='000000',          charset='utf8',  # 千萬注意這裡是 utf8 !          database='django_test_db',          autocommit=True      )      cursor = server.cursor(pymysql.cursors.DictCursor)      return cursor      def get_users():      cursor = get_cursor()  # 連接數據庫        sql = "select * from user_info"  # 把用戶的所有信息查出來(一般不會把密碼放回給前端的,這裡只是為了做演示)      affect_rows = cursor.execute(sql)      user_list = cursor.fetchall()      return user_list    

templates/get_users.html 用戶信息展示頁面

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Title</title>      <!--  引入jquery bootstrap 文件的 CDN  -->      <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>      <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">      <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>  </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 table-hover table-bordered table-striped">                      <thead>                      <tr>                          <th>id</th>                          <th>username</th>                          <th>password</th>                      </tr>                      </thead>                      <tbody>                      <!-- jinja2 的模版語法(for循環) -->                      {%for user_dict in user_list%}                      <tr>                          <td>{{user_dict.id}}</td>                          <td>{{user_dict.username}}</td>                          <td>{{user_dict.password}}</td>                      </tr>                      {%endfor%}                      </tbody>                  </table>              </div>            </div>      </div>  </body>  </html>  

用瀏覽器訪問 http://127.0.0.1:8080/get_users,重啟服務器,在切回瀏覽器即可看到頁面效果

推導流程與小總結

1.純手擼web框架      1.手動書寫socket代碼      2.手動處理http數據    2.基於wsgiref模塊幫助我們處理scoket以及http數據(頂掉上面的歩鄹)  	wsgiref模塊      	1.請求來的時候 解析http數據幫你打包成一個字典傳輸給你 便於你操作各項數據      	2.響應走的時候 自動幫你把數據再打包成符合http協議格式的樣子 再返回給前端    3.封裝路由與視圖函數對應關係 以及視圖函數文件 網站用到的所有的html文件全部放在了templates文件夾下      1.urls.py 路由與視圖函數對應關係      2.views.py 視圖函數 (視圖函數不單單指函數 也可以是類)      3.templates 模板文件夾    4.基於jinja2實現模板的渲染  	模板的渲染  		後端生成好數據 通過某種方式傳遞給前端頁面使用(前端頁面可以基於模板語法更加快捷簡便使用後端傳過來的數據)  

流程圖

小擴展

在不知道是要 encode 還是 decode 的時候,可以用一下方法

二進制數據對應的肯定是 decode 解碼 成字符串呀

字符串對應的肯定是 encode 編碼成二進制數據呀

數據類型轉換技巧(處理編碼)(數據 + encoding)

# 轉成 bytes 類型  bytes(data, encoding='utf-8')    # 轉成 str 類型  str(data, encoding='utf-8')  

python三大Web主流框架分析對比

Django

大而全,自帶的功能特別特別多,就類似於航空母艦

**缺點:**有時過於笨重(小項目很多用不到)

Flask

短小精悍,自帶的功能特別少,全都是依賴於第三方組件(模塊)

第三方組件特別多 –> 如果把所有的第三方組件加起來,完全可以蓋過django

**缺點:**比較受限於第三方的開發者(可能有bug等)

Tornado

天生的異步非阻塞框架,速度特別快,能夠抗住高並發

​ 可以開發遊戲服務器(但開發遊戲,還是 C 和C++用的多,執行效率更快)

手擼三大部分在框架中的情況對比

前面的手擼推導過程,整個框架過程大致可以分為以下三部分

A:socket處理請求的接收與響應的發送

B:路由與視圖函數

C:模板語法給動態頁面渲染數據

Django

A:用的別人的 wsgiref 模塊 B:自帶路由與視圖函數文件 C:自帶一套模板語法

Flask

A:用的別人的werkzeug 模塊(基於 wsgiref 封裝的) B:自帶路由與視圖函數文件 C:用的別人的jinja2

Tornado

A,B,C全都有自己的實現

Django的下載安裝基本使用

參見我的另一篇博客:Django-下載安裝-配置-創建django項目-三板斧簡單使用