Django 系列部落格(一)
阿新 • • 發佈:2019-01-03
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'