Web開發的早期階段,開發者需要手動編寫每個頁面,例如一個新聞入口網站,每天都要修改它的HTML頁面,隨著網站規模和體量的增大,這種做法一定是非常糟糕的。為了解決這個問題,開發人員想到了用程式來為Web伺服器生成動態內容,也就是說網頁中的動態內容不再透過手動編寫而是透過程式自動生成。最早的時候,這項技術被稱為CGI(公共閘道器介面),當然隨著時間的推移,CGI暴露出的問題也越來越多,例如大量重複的樣板程式碼,總體效能較為低下等。在時代呼喚新英雄的背景下,PHP、ASP、JSP這類Web應用開發技術在上世紀90年代中後期如雨後春筍般湧現。通常我們說的Web應用是指透過瀏覽器來訪問網路資源的應用程式,因為瀏覽器的普及性以及易用性,Web應用使用起來方便簡單,免除了安裝和更新應用程式帶來的麻煩;站在開發者的角度,也不用關心使用者使用什麼樣的作業系統,甚至不用區分是PC端還是移動端。
下圖向我們展示了Web應用的工作流程,其中涉及到的術語如下表所示。
說明:相信有經驗的讀者會發現,這張圖中其實還少了很多東西,例如反向代理伺服器、資料庫伺服器、防火牆等,而且圖中的每個節點在實際專案部署時可能是一組節點組成的叢集。當然,如果你對這些沒有什麼概念也不要緊,繼續下去就行了,後面會給大家一一講解的。
術語 | 解釋 |
---|---|
URL/URI | 統一資源定位符/統一資源識別符號,網路資源的唯一標識 |
域名 | 與Web伺服器地址對應的一個易於記憶的字串名字 |
DNS | 域名解析服務,可以將域名轉換成對應的IP地址 |
IP地址 | 網路上的主機的身份標識,透過IP地址可以區分不同的主機 |
HTTP | 超文字傳輸協議,構建在TCP之上的應用級協議,全球資訊網資料通訊的基礎 |
反向代理 | 代理客戶端向伺服器發出請求,然後將伺服器返回的資源返回給客戶端 |
Web伺服器 | 接受HTTP請求,然後返回HTML檔案、純文字檔案、影象等資源給請求者 |
Nginx | 高效能的Web伺服器,也可以用作反向代理,負載均衡 和 HTTP快取 |
這裡我們先費一些筆墨來說說HTTP這個協議。HTTP(超文字傳輸協議)是構建於TCP(傳輸控制協議)之上應用級協議,它利用了TCP提供的可靠的傳輸服務實現了Web應用中的資料交換。按照維基百科上的介紹,設計HTTP最初的目的是為了提供一種釋出和接收HTML頁面的方法,也就是說這個協議是瀏覽器和Web伺服器之間傳輸的資料的載體。關於這個協議的詳細資訊以及目前的發展狀況,大家可以閱讀《HTTP 協議入門》、《網際網路協議入門》系列以及《圖解HTTPS協議》這幾篇文章進行了解。下圖是我在四川省網路通訊技術重點實驗室學習和工作期間使用開源協議分析工具Ethereal(抓包工具WireShark的前身)擷取的訪問百度首頁時的HTTP請求和響應的報文(協議資料),由於Ethereal擷取的是經過網路介面卡的資料,因此可以清晰的看到從物理鏈路層到應用層的協議資料。
HTTP請求(請求行+請求頭+空行+[訊息體]):
HTTP響應(響應行+響應頭+空行+訊息體):
說明:這兩張圖是在2009年9月10日凌晨獲得的,但願這兩張如同泛黃的照片般的截圖能幫助你瞭解HTTP到底是什麼樣子的。當然,如果沒有專業的抓包工具,也可以透過瀏覽器提供的“開發者工具”來檢視HTTP請求和響應的資料格式。
Python的Web框架有上百個,比它的關鍵字還要多。所謂Web框架,就是用於開發Web伺服器端應用的基礎設施,說得通俗一點就是一系列封裝好的模組和工具。事實上,即便沒有Web框架,我們仍然可以透過socket或CGI來開發Web伺服器端應用,但是這樣做的成本和代價在商業專案中通常是不能接受的。透過Web框架,我們可以化繁為簡,降低建立、更新、擴充套件應用程式的工作量。剛才我們說到Python有上百個Web框架,這些框架包括Django、Flask、Tornado、Sanic、Pyramid、Bottle、Web2py、web.py等。
在上述Python的Web框架中,Django無疑是最有代表性的重量級選手,開發者可以基於Django快速的開發可靠的Web應用程式,因為它減少了Web開發中不必要的開銷,對常用的設計和開發模式進行了封裝,並對MVC架構提供了支援(Django中稱之為MTV架構)。MVC是軟體系統開發領域中一種放之四海而皆準的架構,它將系統中的元件分為模型(Model)、檢視(View)和控制器(Controller)三個部分並藉此實現模型(資料)和檢視(顯示)的解耦合。由於模型和檢視進行了分離,所以需要一箇中間人將解耦合的模型和檢視聯絡起來,扮演這個角色的就是控制器。稍具規模的軟體系統都會使用MVC架構(或者是從MVC演進出的其他架構),Django專案中我們稱之為MTV,MTV中的M跟MVC中的M沒有區別,就是代表資料的模型,T代表了網頁模板(顯示資料的檢視),而V代表了檢視函式,在Django框架中,檢視函式和Django框架本身一起扮演了MVC中C的角色。
Django框架誕生於2003年,它是一個在真正的應用中成長起來的專案,由勞倫斯出版集團旗下線上新聞網站的內容管理系統(CMS)研發團隊(主要是Adrian Holovaty和Simon Willison)開發,以比利時的吉普賽爵士吉他手Django Reinhardt來命名。Django框架在2005年夏天作為開源框架釋出,使用Django框架能用很短的時間構建出功能完備的網站,因為它代替程式設計師完成了那些重複乏味的勞動,剩下真正有意義的核心業務給程式設計師來開發,這一點就是對DRY(Don't Repeat Yourself)理念的最好踐行。許多成功的網站和應用都是基於Python語言進行開發的,國內比較有代表性的網站包括:知乎、豆瓣網、果殼網、搜狐閃電郵箱、101圍棋網、海報時尚網、背書吧、堆糖、手機搜狐網、咕咚、愛福窩、果庫等,其中不乏使用了Django框架的產品。
-
檢查Python環境:Django 1.11需要Python 2.7或Python 3.4以上的版本;Django 2.0需要Python 3.4以上的版本;Django 2.1和2.2需要Python 3.5以上的版本;Django 3.0需要Python 3.6以上版本。
說明:Django框架不同版本所需的Python直譯器環境,可以在Django官方文件的FAQ中找到。
可以在macOS的終端中輸入下面的命令檢查Python直譯器版本,Windows系統可以在命令列提示符中輸入
python --version
。
python3 --version
也可以在Python的互動式環境中執行下面的程式碼來檢視Python直譯器的版本。
```Shell
import sys
sys.version
sys.version_info
-
更新包管理工具並安裝Django環境(用於建立Django專案)。
說明:在更新這個文件時,Django最新的正式版本是3.0.7,Django 3.0提供了對ASGI的支援,可以實現全雙工的非同步通訊,但是目前的使用體驗一般,所以暫時不推薦大家使用Django 3.0,下面我們安裝的是Django 2.2.13版本。使用
pip
安裝三方庫和工具時,可以透過==
來指定安裝的版本。pip3 install -U pip pip3 install django==2.2.13
-
檢查Django環境並使用
django-admin
命令建立Django專案(專案名稱為hellodjango)。django-admin --version django-admin startproject hellodjango
-
用PyCharm開啟建立好的Djang專案,併為其新增虛擬環境。
如上圖所示,PyCharm的專案瀏覽器中,最頂層的資料夾
hellodjango
是Python專案資料夾,這個資料夾的名字並不重要,Django專案也不關心這個資料夾叫什麼名字。該資料夾下有一個同名的資料夾,它是Django專案資料夾,其中包含了__init__.py
、settings.py
、urls.py
、wsgi.py
四個檔案,與名為hellodjango
的Django專案資料夾同級的還有一個名為manage.py
的檔案,這些檔案的作用如下所示:hellodjango/__init__.py
:空檔案,告訴Python直譯器這個目錄應該被視為一個Python的包。hellodjango/settings.py
:Django專案的配置檔案。hellodjango/urls.py
:Django專案的URL對映宣告,就像是網站的“目錄”。hellodjango/wsgi.py
:專案執行在WSGI相容Web伺服器上的入口檔案。manage.py
: 管理Django專案的指令碼程式。
說明:WSGI全稱是Web伺服器閘道器介面,維基百科上給出的解釋是“為Python語言定義的Web伺服器和Web應用程式或框架之間的一種簡單而通用的介面”。
建立虛擬環境的介面如下圖所示。
-
安裝專案依賴項。
方法一:開啟PyCharm的終端,在終端中透過
pip
命令安裝Django專案的依賴項。說明:由於已經基於Python 3直譯器環境為專案建立了虛擬環境,所以虛擬環境中的
python
命令對應的是Python 3的直譯器,而pip
命令對應的是Python 3的包管理工具。pip install django==2.2.13
方法二:在PyCharm的偏好設定中,可以找到專案的直譯器環境和已經安裝的三方庫,可以透過點選新增按鈕來安裝新的依賴項,需要提醒大家的是在安裝Django依賴項時,需要指定版本號,否則將預設安裝更新本文時最新的3.0.7版本。
下圖展示了Django版本和Python版本的對應關係,請大家自行對號入座。
Django版本 Python版本 1.8 2.7、3.2、3.3、3.4、3.5 1.9、1.10 2.7、3.4、3.5 1.11 2.7、3.4、3.5、3.6、3.7(Django 1.11.17) 2.0 3.4、3.5、3.6、3.7 2.1 3.5、3.6、3.7 2.2 3.5、3.6、3.7、3.8(Django 2.2.8) 3.0 3.6、3.7、3.8 -
啟動Django自帶的伺服器執行專案。
方法一:在“Run”選單選擇“Edit Configuration”,配置“Django server”執行專案(適用於專業版PyCharm)。
方法二:在“Run”選單選擇“Edit Configuration”,配置執行“Python”程式執行專案(適用於專業版和社群版PyCharm)。
方法三:在PyCharm的終端(Terminal)中透過命令執行專案(適用於專業版和社群版PyCharm)。
python manage.py runserver
-
檢視執行效果。
在瀏覽器中輸入http://127.0.0.1:8000
訪問我們的伺服器,效果如下圖所示。
說明:
- 剛剛啟動的Django自帶的伺服器只能用於開發和測試環境,因為這個伺服器是純Python編寫的輕量級Web伺服器,不適合在生產環境中使用。
- 如果修改了程式碼,不需要為了讓修改的程式碼生效而重新啟動Django自帶的伺服器。但是,在新增新的專案檔案時,該伺服器不會自動重新載入,這個時候就得手動重啟伺服器。
- 可以在終端中透過
python manage.py help
命令檢視Django管理指令碼程式可用的命令引數。- 使用
python manage.py runserver
啟動伺服器時,可以在後面新增引數來指定IP地址和埠號,預設情況下啟動的伺服器將執行在本機的8000
埠。- 在終端中執行的伺服器,可以透過Ctrl+C來停止它 。透過PyCharm的“執行配置”執行的伺服器直接點選視窗上的關閉按鈕就可以終止伺服器的執行。
- 不能在同一個埠上啟動多個伺服器,因為會導致地址的衝突(埠是對IP地址的擴充套件,也是計算機網路地址的一部分)。
-
修改專案的配置檔案
settings.py
。Django是一個支援國際化和本地化的框架,因此剛才我們看到的Django專案的預設首頁也是支援國際化的,我們可以透過修改配置檔案將預設語言修改為中文,時區設定為東八區。
找到修改前的配置(在
settings.py
檔案第100行以後)。LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC'
修改為以下內容。
LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Chongqing'
重新整理剛才的頁面,可以看到修改語言程式碼和時區之後的結果。
如果要開發自己的Web應用,需要先在Django專案中建立“應用”,一個Django專案可以包含一個或多個應用。
-
在PyCharm的終端中執行下面的命令,建立名為
first
的應用。python manage.py startapp first
執行上面的命令會在當前路徑下建立
first
目錄,其目錄結構如下所示:__init__.py
:一個空檔案,告訴Python直譯器這個目錄應該被視為一個Python的包。admin.py
:可以用來註冊模型,用於在Django框架自帶的管理後臺中管理模型。apps.py
:當前應用的配置檔案。migrations
:存放與模型有關的資料庫遷移資訊。__init__.py
:一個空檔案,告訴Python直譯器這個目錄應該被視為一個Python的包。
models.py
:存放應用的資料模型(MTV中的M)。tests.py
:包含測試應用各項功能的測試類和測試函式。views.py
:處理使用者HTTP請求並返回HTTP響應的函式或類(MTV中的V)。
-
修改應用目錄下的檢視檔案
views.py
。from django.http import HttpResponse def show_index(request): return HttpResponse('<h1>Hello, Django!</h1>')
-
修改Django專案目錄下的
urls.py
檔案,將檢視函式和使用者在瀏覽器中請求的路徑對應。from django.contrib import admin from django.urls import path, include from first.views import show_index urlpatterns = [ path('admin/', admin.site.urls), path('hello/', show_index), ]
-
重新執行專案,並開啟瀏覽器中訪問
http://127.0.0.1:8000/hello/
。 -
上面我們透過程式碼為瀏覽器生成了內容,但仍然是靜態內容,如果要生成動態內容,可以修改
views.py
檔案並新增如下所示的程式碼。from random import sample from django.http import HttpResponse def show_index(request): fruits = [ 'Apple', 'Orange', 'Pitaya', 'Durian', 'Waxberry', 'Blueberry', 'Grape', 'Peach', 'Pear', 'Banana', 'Watermelon', 'Mango' ] selected_fruits = sample(fruits, 3) content = '<h3>今天推薦的水果是:</h3>' content += '<hr>' content += '<ul>' for fruit in selected_fruits: content += f'<li>{fruit}</li>' content += '</ul>' return HttpResponse(content)
-
重新整理頁面檢視程式的執行結果,看看每次重新整理的網頁的時候,是不是可以看到不一樣的內容。
上面透過拼接HTML程式碼的方式為瀏覽器生成動態內容的做法在實際開發中是無能接受的,因為實際專案中的前端頁面可能非常複雜,無法用這種拼接動態內容的方式來完成,這一點大家一定能夠想到。為了解決這個問題,我們可以提前準備一個模板頁(MTV中的T),所謂模板頁就是一個帶佔位符和模板指令的HTML頁面。
Django框架中有一個名為render
的便捷函式可以來完成渲染模板的操作。所謂的渲染就是用資料替換掉模板頁中的模板指令和佔位符,當然這裡的渲染稱為後端渲染,即在伺服器端完成頁面的渲染再輸出到瀏覽器中。後端渲染的做法在Web應用的訪問量較大時,會讓伺服器承受較大的負擔,所以越來越多的Web應用會選擇前端渲染的方式,即伺服器只提供頁面所需的資料(通常是JSON格式),在瀏覽器中透過JavaScript程式碼獲取這些資料並渲染頁面上。關於前端渲染的內容,我們會在後續的課程中為大家講解,目前我們使用的是透過模板頁進行後端渲染的做法,具體步驟如下所示。
使用模板頁的步驟如下所示。
-
在專案目錄下建立名為templates資料夾。
-
新增模板頁
index.html
。說明:實際專案開發中,靜態頁由前端開發者提供,後端開發者需要將靜態頁修改為模板頁,以便透過Python程式對其進行渲染,這種做法就是上面提到的後端渲染。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首頁</title> <style> #fruits { font-size: 1.25em; } </style> </head> <body> <h1>今天推薦的水果是:</h1> <hr> <ul id="fruits"> {% for fruit in fruits %} <li>{{ fruit }}</li> {% endfor %} </ul> </body> </html>
在上面的模板頁中我們使用了
{{ fruit }}
這樣的模板佔位符語法,也使用了{% for %}
這樣的模板指令,這些都是Django模板語言(DTL)的一部分。關於模板語法和指令,大家可以看看官方文件,相信這些內容還是很容易理解的,並不需要過多的贅述,大家也可以參考官方文件瞭解模板指令和語法。 -
修改
views.py
檔案,呼叫render
函式渲染模板頁。from random import sample from django.shortcuts import render def show_index(request): fruits = [ 'Apple', 'Orange', 'Pitaya', 'Durian', 'Waxberry', 'Blueberry', 'Grape', 'Peach', 'Pear', 'Banana', 'Watermelon', 'Mango' ] selected_fruits = sample(fruits, 3) return render(request, 'index.html', {'fruits': selected_fruits})
render
函式的第一個引數是請求物件request,第二個引數是我們要渲染的模板頁的名字,第三個引數是要渲染到頁面上的資料,我們透過一個字典將資料交給模板頁,字典中的鍵就是模板頁中使用的模板指令或佔位符中的變數名。 -
到此為止,檢視函式中的
render
還無法找到模板檔案index.html
,需要修改settings.py
檔案,配置模板檔案所在的路徑。修改settings.py
檔案,找到TEMPLATES
配置,修改其中的DIRS
配置。TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates'), ], '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', ], }, }, ]
-
重新執行專案或直接重新整理頁面檢視結果。
至此,我們已經利用Django框架完成了一個非常小的Web應用,雖然它並沒有任何的實際價值,但是可以透過這個專案對Django框架有一個感性的認識。學習Django最好的資料肯定是它的官方文件,官方文件提供了對多國語言的支援,而且有新手教程引導初學者學習使用Django框架,建議大家透過閱讀Django的官方文件來學習和使用這個框架。當然圖靈社群出版的《Django基礎教程》也是非常適合初學者的入門級讀物,有興趣的讀者可以點選連結進行購買。