Django 系列博客(一)
前言
學習了 python 這么久,終於到了Django 框架。這可以說是 python 名氣最大的web 框架了,那么從今天開始會開始從 Django框架的安裝到使用一步步的學習,這系列博客不會像前端的那樣水了(立個 flag),希望可以成為高質量的博客。那么本篇博客介紹 Django 的安裝以及如何在電腦上運行第一個 Django 應用。
Django 的安裝
Django 的安裝很簡單,在 win 和 mac 上面都可以使用 pip 安裝命令安裝,也可以通過 pycharm 安裝,或者下載文件在命令行使用安裝工具安裝。
接下來我在 ubuntu 上安裝做示例。

在這里還安裝了一個依賴包 pytz。這是用來做時區轉換的一個第三方庫。
其他平台的 pip 安裝方式一樣,不過要選用 python3的對應 pip 來安裝,因為現在的 Django 版本已經不支持 python2了。
虛擬環境的安裝
什么是虛擬環境
- 對真實的 python 解釋器的一個拷貝版本
- 事實有效,可以獨立存在並運行解釋 python 代碼
- 可以在計算機上拷貝多個虛擬環境
為什么要使用虛擬環境
- 保證真實環境的純凈性
- 框架的多版本共存
- 方便做框架的版本迭代
- 降低多框架共存的維護成本
安裝虛擬環境
- 通過 pip 安裝虛擬環境庫

因為我之前已經下載好了,所以這里直接顯示請求已經完成,並且后面是安裝的絕對路徑。
- 前往目標文件夾
這個文件夾是你用來保存虛擬環境的文件夾,該文件夾一旦確定就不要輕易更改。

這個 py3-env1是創建的一個純凈虛擬環境。
- 創建純凈的虛擬環境
virtualenv 虛擬環境名 (py3-env2)
- 終端啟動虛擬環境
cd py3-env1\Scripts
activate
- 進入虛擬環境下的 python 開發環境
python3
- 關閉虛擬環境
deactivate
- 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 連接稱之為一個事務,過程可以分為四步
- 客戶端與服務端建立連接
- 客戶端發生一個 HTTP 協議指定格式的請求
- 服務端接收請求后,回應一個 HTTP 協議指定格式的響應
- 客戶端將服務端的響應展現給用戶
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'
