1. 程式人生 > >Django 系列部落格(一)

Django 系列部落格(一)

Django 系列部落格(一)

前言

學習了 python 這麼久,終於到了Django 框架。這可以說是 python 名氣最大的web 框架了,那麼從今天開始會開始從 Django框架的安裝到使用一步步的學習,這系列部落格不會像前端的那樣水了(立個 flag),希望可以成為高質量的部落格。那麼本篇部落格介紹 Django 的安裝以及如何在電腦上執行第一個 Django 應用。

Django 的安裝

Django 的安裝很簡單,在 win 和 mac 上面都可以使用 pip 安裝命令安裝,也可以通過 pycharm 安裝,或者下載檔案在命令列使用安裝工具安裝。

接下來我在 ubuntu 上安裝做示例。

在這裡還安裝了一個依賴包 pytz。這是用來做時區轉換的一個第三方庫。

其他平臺的 pip 安裝方式一樣,不過要選用 python3的對應 pip 來安裝,因為現在的 Django 版本已經不支援 python2了。

虛擬環境的安裝

什麼是虛擬環境

  • 對真實的 python 直譯器的一個拷貝版本
  • 事實有效,可以獨立存在並執行解釋 python 程式碼
  • 可以在計算機上拷貝多個虛擬環境

為什麼要使用虛擬環境

  • 保證真實環境的純淨性
  • 框架的多版本共存
  • 方便做框架的版本迭代
  • 降低多框架共存的維護成本

安裝虛擬環境

  1. 通過 pip 安裝虛擬環境庫

因為我之前已經下載好了,所以這裡直接顯示請求已經完成,並且後面是安裝的絕對路徑。

  1. 前往目標資料夾

這個資料夾是你用來儲存虛擬環境的資料夾,該資料夾一旦確定就不要輕易更改。

image-20190103154126721

這個 py3-env1是建立的一個純淨虛擬環境。

  1. 建立純淨的虛擬環境
virtualenv 虛擬環境名 (py3-env2)
  1. 終端啟動虛擬環境
cd py3-env1\Scripts
activate
  1. 進入虛擬環境下的 python 開發環境
python3
  1. 關閉虛擬環境
deactivate
  1. Pycharm的開發配置
新增:建立專案 -> Project Interpreter -> Existing interpreter -> Virtualenv Environment | System Interpreter -> 目標路徑下的 python.exe
刪除:Setting -> Project -> Project Interpreter -> Show All

mac 電腦從第三步直接到最後一步就好

瞭解

# 建立非純淨環境:
#   -- virtualenv-clone 本地環境 虛擬環境名
# Mac配置終端,在終端執行虛擬環境
# 在使用者根目錄下的.bash_profile(沒有需手動建立)檔案中設定
# alias 終端指令名(env-py3)='/Library/Virtualenv/虛擬環境名/bin/python3'
# alias 終端指令名(env-pip3)='/Library/Virtualenv/虛擬環境名/bin/pip3'

HTTP 協議

因為 Django 框架應用層是採用的 HTTP 協議,所以有必要了解 HTTP 協議。

什麼是 HTTP 協議

  • HTTP(HyperText Transport Protocol) 是超文字傳輸協議,而 HTTPS 也歸屬於 HTTP 協議,S 代表安全。
  • 基於 TCP/IP協議基礎上的應用層協議,底層實現仍為 socket
  • 基於請求-響應模式:通訊一定是從客戶端開始,服務端接收到客戶端一定會做出對應響應
  • 無狀態:協議不對任何一次通訊狀態和任何資料做儲存
  • 無連線:一次連線只完成一次請求-響應,請求-響應完畢後會立即斷開連線。

HTTP 工作原理

一次 HTTP 連線稱之為一個事務,過程可以分為四步

  1. 客戶端與服務端建立連線
  2. 客戶端發生一個 HTTP 協議指定格式的請求
  3. 服務端接收請求後,迴應一個 HTTP 協議指定格式的響應
  4. 客戶端將服務端的響應展現給使用者

HTTP 狀態碼

  • 1開頭:

  • 2開頭:

  • 3開頭:

  • 4開頭:

  • 5開頭:

請求報文

# 請求行  請求頭  請求體
'''
POST / HTTP/1.1\r\n
Host: 127.0.0.1:8001\r\n
Connection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 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
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n
\r\n
usr=abc&pwd=123
'''

響應報文

# 響應行  響應頭  響應體
'''
HTTP/1.1 200 OK\r\n
Content-type:text/html\r\n
\r\n
Login Success
'''

使用原生 socket 完成和瀏覽器的互動

目錄結構

01_socket
    -- 01_client.html:前臺通過form表單訪問後臺的頁面
    -- 01_login.html:後臺測試渲染給前臺的登入頁面
    -- 01_index.html:後臺測試渲染給前臺的主頁
    -- 01_server.py:後臺伺服器檔案

因為 B/S架構的客戶端已經由瀏覽器寫好,所以只需要關注伺服器端就ok。

伺服器端程式碼

from socket import socket

# 設定響應頭(包含響應行)
RESP_HEADER = b'HTTP/1.1 200 OK\r\nContent-type:text/html;charset=utf-8\r\n\r\n' # 連續兩個\r\n表示響應頭結束

# 設定伺服器 socket 相關資訊
server = socket()
server.bind('', 8080) # 空字串表示繫結本機
server.listen(5)
print(('服務:http://localhost:8080'))

while True:
    # 獲取以 http 協議發來的請求
    client, addr = server.accept()
    data = client.recv(1024)
    # 資料報文 包含請求行 請求頭 請求體
    print(data)
    client.send(RESP_HEADER)
    
    # /index => 響應主頁
    # /login => 登入頁面
    # 錯誤 => 404
    # 資料 data, 位元組形式 => 字串形式
    strData = str(data, encodeing)
    
    # 解析請求的資料,分析得到路由
    my_route = strData.split('\r\n')[0].split(' ')[1]
    
    # 後臺沒有設定的路由,統統由404來處理
    dt = b'404'
    
    # 設定的路由返回響應的頁面檔案
    if my_route == '/index':
        with open('index 頁面路徑', 'rb') as f:
            dt = f.read()
    if my_route == '/login':
        with open('login 頁面路徑', 'rb') as f:
            dt = f.read()
            
    # /favicon.ico該請求是往後臺請求標籤圖示
    if my_route == '/favicon.ico':
        with open('favicon.ico', 'rb') as f:
            dt = f.read()
    # 伺服器傳送響應體
    client.send(dt)
    # 一次迴圈,代表一次響應,也就是一次事務的完成,要關閉 http 請求連線
    client.close()

修改返回資料,完善響應體

# 字串
client.send(b'HTTP/1.1 200 OK\r\n')
client.send(b'\r\n')
client.send(b'Normal Socket Web')
# html程式碼,請求頭要設定支援 html 程式碼
client.send(b'HTTP/1.1 200 OK\r\n')
client.send(b'Content-type:text/html\r\n')
client.send(b'\r\n')
client.send(b'<h1>Normal Socket Web</h1>')
# html檔案(同級目錄建立一個index.html頁面)
client.send(b'HTTP/1.1 200 OK\r\n')
client.send(b'Content-type:text/html\r\n')
client.send(b'\r\n')
# 利用檔案方式讀取頁面
with open('01_index.html', 'rb') as f:
    dt = f.read()
client.send(dt)

拓展

修改接收資料,模擬後臺路由

# 分析接收到的資料
data = client.recv(1024)
# 保證接收到的資料作為字串進行以下處理
data = str(data, encoding='utf-8')
# 拆分出地址位
route = data.split('\r\n')[0].split(' ')[1]
# 匹配地址,做出不同的響應
if route == '/index':
    with open('01_index.html', 'rb') as f:
        dt = f.read()
elif route == '/login':
    with open('01_login.html', 'rb') as f:
        dt = f.read()
else:
    dt = b'404'
client.send(dt)

框架演變

目錄結構

02_frame
    -- favicon.ico
    -- index.html
    -- manage.py

manage.py

import socket
import pymysql
# 響應頭
RESP_HEADER = b'HTTP/1.1 200 OK\r\nContent-type:text/html\r\n\r\n'

# 請求處理
def index():
    # 以位元組方式讀取檔案
    with open('index.html', 'rb') as f:
        dt = f.read()
    return dt
def ico():
    with open('favicon.ico', 'rb') as f:
        dt = f.read()
    return dt
def user():
    # 資料庫操作
    conn = pymysql.connect(host='127.0.0.1', port=3306, db='django', user='root', password='root')
    cur = conn.cursor(pymysql.cursors.DictCursor)
    cur.execute('select * from user')
    users = cur.fetchall()
    print(users)
    users = '''%d:%s
    %d:%s''' % (users[0]['id'], users[0]['name'], users[1]['id'], users[1]['name'])
    return users.encode('utf-8')

# 設定路由
urls = {
    # 請求路徑與請求處理函式一一對應
    '/index': index,
    '/favicon.ico': ico,
    '/user': user
}

# 設定socket
def serve(host, port):
    server = socket.socket()
    server.bind((host, port))
    print('start:http://' + host + ':' + str(port))
    server.listen(5)
    while True:
        sock, addr = server.accept()
        data = sock.recv(1024)
        data = str(data, encoding='utf-8')
        print(data)
        route = data.split('\r\n')[0].split(' ')[1]

        resp = b'404'
        if route in urls:
            resp = urls[route]()

        sock.send(RESP_HEADER)
        sock.send(resp)
        sock.close()

# 啟服務
if __name__ == '__main__':
    serve('127.0.0.1', 8002)

專案演變

目錄結構

03_proj
    -- template
        -- index.html
        -- user.html
    favicon.ico
    start.py
    urls.py
    views.py

index.html

<h1>{{ name }}</h1>

user.html

<table border="1">
    <tr>
        <th>id</th>
        <th>name</th>
        <th>password</th>
    </tr>
    {% for user in users%}
    <tr>
        <td>{{user.id}}</td>
        <td>{{user.name}}</td>
        <td>{{user.password}}</td>
    </tr>
    {% endfor %}
</table>

start.py

from wsgiref.simple_server import make_server
from urls import urls


def app(env, response):
    print(env)
    # 設定響應頭
    response("200 OK", [('Content-type', 'text/html')])
    route = env['PATH_INFO']
    print(route)
    data = urls['error']()
    if route in urls:
        data = urls[route]()
    # 返回二進位制響應體
    return [data]


if __name__ == '__main__':
    server = make_server('127.0.0.1', 8003, app)
    print('start:http://127.0.0.1:8003')
    server.serve_forever()

urls.py

from views import *
urls = {
    '/index': index,
    '/favicon.ico': ico,
    '/user': user,
    'error': error
}

views.py

import pymysql
# 利用jinja2來渲染模板,將後臺資料傳給前臺
from jinja2 import Template

def index():
    with open('templates/index.html', 'r') as f:
        dt = f.read()
    tem = Template(dt)
    resp = tem.render(name='主頁')
    return resp.encode('utf-8')

def ico():
    with open('favicon.ico', 'rb') as f:
        dt = f.read()
    return dt

def user():
    # 資料庫操作
    conn = pymysql.connect(host='127.0.0.1', port=3306, db='django', user='root', password='root')
    cur = conn.cursor(pymysql.cursors.DictCursor)
    cur.execute('select * from user')
    users = cur.fetchall()
    print(users)

    with open('templates/user.html', 'r') as f:
        dt = f.read()
    tem = Template(dt)
    resp = tem.render(users=users)

    return resp.encode('utf-8')

def error():
    return b'404'