python day51

今日內容


Web框架的本質

咱們能夠這樣理解:全部的Web應用本質上就是一個socket服務端,而用戶的瀏覽器就是一個socket客戶端。 這樣咱們就能夠本身實現Web框架了。html

簡易版本的自定義web框架代碼:

import socket ​ sk = socket.socket() sk.bind(("127.0.0.1", 80)) sk.listen() ​ while True: conn, addr = sk.accept() data = conn.recv(8096) conn.send(b"OK") conn.close()

總結:

用戶的瀏覽器一輸入網址,會給服務端發送數據,那瀏覽器會發送什麼數據?怎麼發?這個誰來定?因此,有一個統一的規則,讓你們發送消息、接收消息的時候有個格式依據,不能隨便寫。這個規則就是HTTP協議,之後瀏覽器發送請求信息也好,服務器回覆響應信息也罷,都要按照這個規則來。前端

HTTP協議

超文本傳輸協議:規定了客戶端與服務端消息傳輸的格式python

四大特性:

  • 1.基於TCP/IP協議做用於應用層的協議
  • 2.基於請求響應
  • 3.無狀態
  • 4.無鏈接

數據格式之請求:

  • 請求首行
  • 請求頭(一堆k,v鍵值對)
  • /r/n
  • 請求體(post請求攜帶的數據)

數據格式之響應:

  • 響應首行
  • 響應頭(一堆k,v鍵值對)
  • /r/n
  • 響應體(post請求攜帶的數據)

響應狀態碼:

  • 1XX 服務器已經成功接受到你的數據正在處理,你能夠繼續提交其餘數據
  • 2XX 請求成功,服務器已經將你請求的數據發送給你了
  • 3XX 重定向
  • 4XX 請求資源不存在
  • 5XX 服務器錯誤

HTTP協議主要規定了客戶端和服務器之間的通訊格式,讓咱們首先打印下咱們在服務端接收到的消息是什麼。

# b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nDNT: 1\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zv\r\n\r\n
接收的原始數據

咱們發現收發的消息須要按照必定的格式來:

''' b'GET / HTTP/1.1\r\n Host: 127.0.0.1:8080\r\n Connection: keep-alive\r\n Upgrade-Insecure-Requests: 1\r\n User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\r\n Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n DNT: 1\r\n Accept-Encoding: gzip, deflate, br\r\n Accept-Language: zh-CN,zh;q=0.9\r\n Cookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zv\r\n \r\n '''

結論:

以前那些看起來比較雜亂的數據,變成了一排排的鍵值對,具體名稱見上.

增強版的自定義版web框架:

import socket ​ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('127.0.0.1', 8000)) sock.listen() ​ while True: conn, addr = sock.accept() data = conn.recv(8096) # 給回覆的消息加上響應狀態行
    conn.send(b"HTTP/1.1 200 OK\r\n\r\n") conn.send(b"OK") conn.close()

這樣咱們經過十幾行代碼就能簡單地演示web 框架的本質。mysql

根據不一樣的路徑返回不一樣的內容

import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口
sk.listen()  # 監聽
while 1: # 等待鏈接
    conn, add = sk.accept() data = conn.recv(8096)  # 接收客戶端發來的消息
    # 從data中取到路徑
    data = str(data, encoding="utf8")  # 把收到的字節類型的數據轉換成字符串
    # 按\r\n分割
    data1 = data.split("\r\n")[0] url = data1.split()[1]  # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行
    # 根據不一樣的路徑返回不一樣內容
    if url == "/index/": response = b"index"
    elif url == "/home/": response = b"home"
    else: response = b"404 not found!" ​ conn.send(response) conn.close()

根據不一樣的路徑返回不一樣的內容--函數版

import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口
sk.listen()  # 監聽
# 將返回不一樣的內容部分封裝成函數
def index(url): s = "這是{}頁面!".format(url) return bytes(s, encoding="utf8") ​ def home(url): s = "這是{}頁面!".format(url) return bytes(s, encoding="utf8") ​ while 1: # 等待鏈接
    conn, add = sk.accept() data = conn.recv(8096)  # 接收客戶端發來的消息
    # 從data中取到路徑
    data = str(data, encoding="utf8")  # 把收到的字節類型的數據轉換成字符串
    # 按\r\n分割
    data1 = data.split("\r\n")[0] url = data1.split()[1]  # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行
    # 根據不一樣的路徑返回不一樣內容,response是具體的響應體
    if url == "/index/": response = index(url) elif url == "/home/": response = home(url) else: response = b"404 not found!" ​ conn.send(response) conn.close()

根據不一樣的路徑返回不一樣的內容--函數進階版

import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口
sk.listen()  # 監聽
# 將返回不一樣的內容部分封裝成函數
def index(url): s = "這是{}頁面!".format(url) return bytes(s, encoding="utf8") ​ def home(url): s = "這是{}頁面!".format(url) return bytes(s, encoding="utf8") ​ # 定義一個url和實際要執行的函數的對應關係
list1 = [ ("/index/", index), ("/home/", home), ] ​ while 1: # 等待鏈接
    conn, add = sk.accept() data = conn.recv(8096)  # 接收客戶端發來的消息
    # 從data中取到路徑
    data = str(data, encoding="utf8")  # 把收到的字節類型的數據轉換成字符串
    # 按\r\n分割
    data1 = data.split("\r\n")[0] url = data1.split()[1]  # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行
    # 根據不一樣的路徑返回不一樣內容
    func = None  # 定義一個保存將要執行的函數名的變量
    for i in list1: if i[0] == url: func = i[1] break
    if func: response = func(url) else: response = b"404 not found!"# 返回具體的響應消息
 conn.send(response) conn.close()

返回具體的HTML文件

""" 根據URL中不一樣的路徑返回不一樣的內容--函數進階版 返回獨立的HTML頁面 """import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口
sk.listen()  # 監聽
# 將返回不一樣的內容部分封裝成函數
def index(url): # 讀取index.html頁面的內容
    with open("index.html", "r", encoding="utf8") as f: s = f.read() # 返回字節數據
    return bytes(s, encoding="utf8") ​ def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") ​ # 定義一個url和實際要執行的函數的對應關係
list1 = [ ("/index/", index), ("/home/", home), ] ​ while 1: # 等待鏈接
    conn, add = sk.accept() data = conn.recv(8096)  # 接收客戶端發來的消息
    # 從data中取到路徑
    data = str(data, encoding="utf8")  # 把收到的字節類型的數據轉換成字符串
    # 按\r\n分割
    data1 = data.split("\r\n")[0] url = data1.split()[1]  # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行
    # 根據不一樣的路徑返回不一樣內容
    func = None  # 定義一個保存將要執行的函數名的變量
    for i in list1: if i[0] == url: func = i[1] break
    if func: response = func(url) else: response = b"404 not found!"# 返回具體的響應消息
 conn.send(response) conn.close()

動靜態網頁

靜態網頁:

頁面上的數據都是寫死的,萬年不變web

動態網頁:

  • 頁面上的數據是從後端動態獲取的
  • 好比後端獲取當前時間
  • 後端獲取數據庫數據而後傳遞給前端頁面

製做成動態網頁:

""" 根據URL中不一樣的路徑返回不一樣的內容--函數進階版 返回HTML頁面 讓網頁動態起來 """import socket import time ​ sk = socket.socket() sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口
sk.listen()  # 監聽
# 將返回不一樣的內容部分封裝成函數
def index(url): with open("index.html", "r", encoding="utf8") as f: s = f.read() now = str(time.time()) s = s.replace("@@oo@@", now)  # 在網頁中定義好特殊符號,用動態的數據去替換提早定義好的特殊符號
    return bytes(s, encoding="utf8") ​ def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") ​ # 定義一個url和實際要執行的函數的對應關係
list1 = [ ("/index/", index), ("/home/", home), ] ​ while 1: # 等待鏈接
    conn, add = sk.accept() data = conn.recv(8096)  # 接收客戶端發來的消息
    # 從data中取到路徑
    data = str(data, encoding="utf8")  # 把收到的字節類型的數據轉換成字符串
    # 按\r\n分割
    data1 = data.split("\r\n")[0] url = data1.split()[1]  # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行
    # 根據不一樣的路徑返回不一樣內容
    func = None  # 定義一個保存將要執行的函數名的變量
    for i in list1: if i[0] == url: func = i[1] break
    if func: response = func(url) else: response = b"404 not found!"# 返回具體的響應消息
 conn.send(response) conn.close()

服務器程序和應用程序

  • 對於真實開發中的python web程序來講,通常會分爲兩部分:服務器程序和應用程序。
  • 服務器程序負責對socket服務器進行封裝,並在請求到來時,對請求的各類數據進行整理。

軟件開發架構:

C/S架構:客戶端 服務端

B/S架構:瀏覽器 服務器

本質上b/s架構其實也是c/s架構sql

應用程序則負責具體的邏輯處理。爲了方便應用程序的開發,就出現了衆多的Web框架,例如:Django、Flask、web.py 等。數據庫

Web框架

python三大主流web框架:

  • Django:大而全,自帶了不少功能模塊,相似於航空母艦 (缺點:有點笨重)
  • Flask:短小精悍,自帶的功能模塊特別少,大部分都是依賴於第三方模塊(小而輕)
  • Tornado:異步非阻塞 主要用在處理高io 多路複用的狀況 能夠寫遊戲後端

咱們根據三樣元素來統計他們的特色:

  • a:socket
  • b:路由與視圖函數
  • c:模板渲染

Django:

  • a用的別人的 wsgiref
  • b本身寫的
  • c本身寫的

Flask:

  • a用的別人的 werkzeug
  • b本身寫的
  • c用的別人的 jinja2

Tornado:

a,b,c都是本身寫的django

簡易版本的web請求流程圖

關於使用Django框架的注意事項(重要):

  • 1.計算機的名稱不能有中文
  • 2.一個pycharm窗口就是一個項目,不要多個項目放在一個窗口裏面
  • 3.項目名不能起中文

Django

版本問題以及下載

咱們一般都推薦下載1.11.11版本,在下載時也有兩種方式後端

命令行下載:

  • pip3 install django==1.11.11
  • django-admin(驗證是否下載成功)

pycharm下載:

  • pip3 install django==1.11.11
  • django-admin(驗證是否下載成功)

建立Django項目的方式:

命令行建立

  • 建立django項目:

    • django-admin startproject 項目名
  • 建立app應用:

    • python3 manage.py startapp 應用名
  • 啓動django項目:

    • python3 manage.py runserver

PS:用命令行建立django默認不會自動建立templates文件夾,須要你手動本身建立(注意改文件夾路徑是否被添加配置文件中,以下段代碼中的DIRS:[ ]中添加)

模板文件配置:

TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, "template")],  # template文件夾位置
        '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', ], }, }, ]

靜態文件配置:

STATIC_URL = '/static/'  # HTML中使用的靜態文件夾前綴
STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static"),  # 靜態文件存放位置
]

剛開始學習時可在配置文件中暫時禁用csrf中間件,方便表單提交測試.

MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]

Pycharm建立

建立django項目:

FILE >>> new project 選擇第二個django 須要注意名字不能有中文,選擇本地的解釋器,勾選後臺管理瀏覽器

建立app應用:

  • 1.pycharm命令行建立 >>> python3 manage.py startapp 應用名
  • 2.Tools下面run manage task功能欄

啓動django項目:

點擊上面綠色小箭頭!

注意:

  • 1.用django必定要保證只有一個在運行狀態,切記切記!
  • 2.必定記得清瀏覽器的緩存

app應用的概念

一個django項目就是一所大學,app就是大學裏面的學院

注意新建立的app須要在配置文件中註冊才能生效!

INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config'  # 能夠用全稱
    'app01'                  # 也能夠簡寫
]

Django各個文件的做用

應用名字

  • migrations 數據庫遷移記錄相關數據
  • admin.py Django後臺管理相關
  • models.py 模型表相關
  • views.py 視圖函數相關

項目名

  • settings.py 配置文件
  • urls.py 路由與視圖函數的映射關係

templates

項目用到的全部的html文件

manage.py

Django入口文件


WSGI標準

不一樣的框架有不一樣的開發方式,可是不管如何,開發出的應用程序都要和服務器程序配合,才能爲用戶提供服務。

服務器程序須要爲不一樣的框架提供不一樣的支持。這樣混亂的局面不管對於服務器仍是框架,都是很差的。對服務器來講,須要支持各類不一樣框架,對框架來講,只有支持它的服務器才能被開發出的應用使用。

這時候,標準化就變得尤其重要。咱們能夠設立一個標準,只要服務器程序支持這個標準,框架也支持這個標準,那麼他們就能夠配合使用。一旦標準肯定,雙方各自實現。這樣,服務器能夠支持更多支持標準的框架,框架也可使用更多支持標準的服務器。

WSGI(Web Server Gateway Interface)就是一種規範,它定義了使用Python編寫的web應用程序與web服務器程序之間的接口格式,實現web應用程序與web服務器程序間的解耦。

經常使用的WSGI服務器有uwsgi、Gunicorn。而Python標準庫提供的獨立WSGI服務器叫wsgiref,Django開發環境用的就是這個模塊來作服務器。

引伸出的wsgiref模塊

咱們利用wsgiref模塊來替換咱們本身寫的web框架的socket server部分:

""" 根據URL中不一樣的路徑返回不一樣的內容--函數進階版 返回HTML頁面 讓網頁動態起來 wsgiref模塊版 """import time from wsgiref.simple_server import make_server ​ # 將返回不一樣的內容部分封裝成函數
def index(url): with open("index.html", "r", encoding="utf8") as f: s = f.read() now = str(time.time()) s = s.replace("@@oo@@", now) return bytes(s, encoding="utf8") ​ def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") ​ # 定義一個url和實際要執行的函數的對應關係
list1 = [ ("/index/", index), ("/home/", home), ] ​ def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # 設置HTTP響應的狀態碼和頭信息
    url = environ['PATH_INFO']  # 取到用戶輸入的url
    func = None for i in list1: if i[0] == url: func = i[1] break
    if func: response = func(url) else: response = b"404 not found!"
    return [response, ] ​ if __name__ == '__main__': httpd = make_server('127.0.0.1', 8090, run_server) print("我在8090等你哦...") httpd.serve_forever()

引伸出的jinjia2模塊

上面的代碼實現了一個簡單的動態,我徹底能夠從數據庫中查詢數據,而後去替換我html中的對應內容,而後再發送給瀏覽器完成渲染。 本質上就是HTML內容中利用一些特殊的符號來替換要展現的數據。 我這裏用的特殊符號是我定義的,其實模板渲染有個現成的工具: "jinja2"

上述過程就至關於HTML模板渲染數據。

關於HTML頁面渲染

後端生成的數據直接傳遞給前端頁面使用(而且前端頁面能夠靈活的操做改數據) >>> 模板語法

PS:模板語法,jinja2支持前端直接使用相似於python的語法操做數據

下載jinja2:

# 終端輸入便可,也能夠用命令行輸入
pip install jinja2

index.html文件:

<!--渲染前-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="x-ua-compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Title</title>
</head>
<body>
    <h1>姓名:{{name}}</h1>
    <h1>愛好:</h1>
    <ul> {% for hobby in hobby_list %} <li>{{hobby}}</li> {% endfor %} </ul>
</body>
</html>

渲染index.html文件示例:

from wsgiref.simple_server import make_server from jinja2 import Template ​ def index(): with open("index2.html", "r") as f: data = f.read() template = Template(data)  # 生成模板文件
    ret = template.render({"name": "Alex", "hobby_list": ["燙頭", "泡吧"]})  # 把數據填充到模板裏面
    return [bytes(ret, encoding="utf8"), ] ​ def home(): with open("home.html", "rb") as f: data = f.read() return [data, ] ​ # 定義一個url和函數的對應關係
URL_LIST = [ ("/index/", index), ("/home/", home), ] ​ def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # 設置HTTP響應的狀態碼和頭信息
    url = environ['PATH_INFO']  # 取到用戶輸入的url
    func = None  # 將要執行的函數
    for i in URL_LIST: if i[0] == url: func = i[1]  # 去以前定義好的url列表裏找url應該執行的函數
            break
    if func:  # 若是能找到要執行的函數
        return func()  # 返回函數的執行結果
    else: return [bytes("404沒有該頁面", encoding="utf8"), ] ​ if __name__ == '__main__': httpd = make_server('', 8000, run_server) print("Serving HTTP on port 8000...") httpd.serve_forever()

如今的數據是咱們本身手寫的,咱們也能夠從數據庫中獲取查詢獲取數據填充頁面

使用pymysql鏈接數據庫:

conn = pymysql.connect( host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8" ) cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("select name, age, department_id from userinfo") user_list = cursor.fetchall() cursor.close() conn.close()

建立一個測試的user表:

CREATE TABLE user( id int auto_increment PRIMARY KEY, name CHAR(10) NOT NULL, hobby CHAR(20) NOT NULL )engine=innodb DEFAULT charset=UTF8;

模板的原理就是字符串替換,咱們只要在HTML頁面中遵循jinja2的語法規則寫上,其內部就會按照指定的語法進行相應的替換,從而達到動態的返回內容。

模板替換語法(字典部分)在前端頁面部分的代碼:

<p>{{ user_dic }}</p>
<p>{{ user_dic.name }}</p>
<p>{{ user_dic['password'] }}</p>
<p>{{ user_dic.get('name') }}</p> {% for user in user_dict %}  <!--[{},{},{},{}]-->
        <tr>
            <td>{{ user.id }}</td>
            <td>{{ user.name }}</td>
            <td>{{ user.password }}</td>
        </tr> {% endfor %}

Django基礎必備三件套:

from django.shortcuts import HttpResponse, render, redirect

HttpResponse

內部傳入一個字符串參數,返回給瀏覽器。

示例:

def index(request): # 業務邏輯代碼
    return HttpResponse("OK")

render

除request參數外還接受一個待渲染的模板文件和一個保存具體數據的字典參數。將數據填充進模板文件,最後把結果返回給瀏覽器。(相似於咱們上面用到的jinja2)

示例(兩種方式):

#第一種
def reg(request): user_dict = {'name':'jason','password':'123'} return render(request,'reg.html',{'user_dict':user_dict} ​ #第二種
def reg(request): user_dict = {'name':'jason','password':'123'} return render(request,'reg.html',locals())      

redirect

接受一個URL參數,表示跳轉到指定的URL。

示例:

def index(request):
    # 業務邏輯代碼
    return redirect("/home/")

關於重定向

 

相關文章
相關標籤/搜索