視圖是django的MTV架構模式的V部分,主要負責處理用戶請求和生成響應內容,然後在頁面或其他類型文檔中顯示。也可以理解爲視圖是MVC架構裏面的C部分,主要處理功能和業務上的邏輯,我們習慣使用視圖函數處理HTTP請求,即在視圖定義def函數,這種方式稱爲FBV(Function Base Views )。
1、設置響應方式
1.1 返回響應內容
視圖函數是通過return方式返回,然後生成相應的網頁內容呈現在瀏覽器上,return是Python的內置語法,用於設置函數的返回值,若要設置不同的響應方式,則需要使用Django內置的響應類。如圖:
響應類型 | 說明 |
---|---|
HttpResponse(‘Hello world’) | 狀態碼200,請求已成功被服務器接受 |
HttpResponseRedirect(’/’) | 狀態碼302,重定向首頁地址 |
HttpResponseBadRequest(‘400’) | 狀態碼301,永久重定向首頁地址 |
HttpResponseNotFound(‘404’) | 狀態碼404,網頁不存在或網頁的URL失效 |
HttpResponseForbidden(‘403’) | 狀態碼403,沒有訪問權限 |
HttpResponseNotAllowed(‘405’) | 狀態碼405,不允許使用該請求方式 |
HttpResponse(‘500’) | 狀態碼500,服務器內容錯誤 |
JsonResponse({‘foo’:‘bar’}) | 默認狀態碼200,響應內容爲JSON數據 |
StreamingHttpResponse() | 默認狀態碼200,響應內容以流式輸出 |
不同的響應方式代表不同的HTTP狀態碼,其核心作用是Web Server服務器用來告訴瀏覽器當前網頁發生了什麼事,或者當前Web服務器的響應狀態。上述的響應類主要來自於模塊django.http,該模塊是實現響應功能的核心。以HttpResponse爲例,在MyDjango項目的index文件夾的urls.py和views.py中編寫功能代碼:
# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
# 定義首頁的路由
path('', views.index, name='index'),
]
# index的views.py
from django.http import HttpResponse
from django.shortcuts import render
def index(request):
html = '<h1>Hello World</h1>'
return HttpResponse(html, status=200)
視圖函數index使用響應類HttpResponse實現響應過程。從HttpResponse的參數可知,第一個參數是響應內容,一般是網頁內容或JSON數據,網頁內容是以HTML語言爲主的,JSON數據用於生成API接口數據。第二個參數用於設置HTTP狀態碼,它支持HTTP所有的狀態碼。
從HttpResponse的使用過程可知,如果生成網頁內容,就需要將HTML語言以字符串的形式表示,如果網頁內容過大,就會增加視圖函數的代碼量,同時也沒有體現模板的作用。因此,Django在此基礎上進行了封裝處理,定義了函數render、render_to_response等。
render和render_to_response實現的功能是一致的。render_to_response自2.0版本以來已開始被棄用,但並不代表在2.0以上版本無法使用,只是大部分開發者都使用render。對render進行說明,語法如下:
render(request,template_name,context=None,content_type=None,status=None,using=None)
render的參數request和template_name是必須參數,其餘參數都是可選參數,各個參數如下:
- request: 瀏覽器向服務器發送的請求對象,包含用戶信息、請求內容和請求方式等。
- template_name: 設置模板文件名,用於生成網頁內容。
- context: 對模板上下文(模板變量)賦值,以字典個格式表示,默認情況下是一個空字典。
- content_type: 響應內容的數據格式,一般情況下使用默認值即可。
- status: HTTP狀態碼,默認爲200。
- using: 設置模板引擎,用於解析模板文件,生成網頁內容。
1.2 設置重定向
重定向的狀態碼分爲301和302,前者時永久性跳轉,後者是臨時性跳轉的,兩者的區別在於搜索引擎的網頁抓取。301重定向是永久的重定向,搜索引擎在抓取新內容的同時會將舊的網址替換爲重定向之後的網址。302跳轉時暫時的跳轉,搜索引擎會抓取新內容而保留舊的網址。因爲服務器返回302代碼,所以搜索引擎認爲新的網址只是暫時的。
主要講述redirect函數,函數時將HttpResponseRedirect和HttpResponsePermanentRedirect的功能進行完善和組合。 重定向類HttpResponseRedirect和HttpResponsePermanentRedirect分別代表HTTP狀態碼302和301。從redirect的定義過程可以看出,該函數運行原理如下:
- 判斷參數permanent的真假性來選擇重定向的函數,若爲True,則調用HttpResponsePermanentRedirect(301)來完成重定向過程,若爲False,則調用HttpResponseRedirect(302)。
- 由於HttpResponsePermanentRedirect和HttpResponseRedirect只支持路由地址的傳入,因此函數redirect調用resolve_url方法對參數to進行判斷。若參數to是路由地址,則直接將參數to的參數值返回;若參數to是路由命名,則使用reverse函數轉換路由地址;若參數to是模型對象,則將模型轉換成相應的路由地址(這種方法的使用頻率相對較低)。
示例如下:
# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
# 定義首頁的路由
path('', views.index, name='index'),
# 定義商城的路由
path('shop', views.shop, name='shop')
]
# index的views.py
from django.http import HttpResponseRedirect
from django.http import HttpResponsePermanentRedirect
from django.shortcuts import reverse
from django.shortcuts import render, redirect
def index(request):
return redirect('index:shop' ,permanent=True)
# 設置302的重定向
# url = reverse('index:shop')
# return HttpResponseRedirect(url)
# 設置301的重定向
# return HttpResponsePermanentRedirect(url)
def shop(request):
return render(request, 'index.html')
1.3 異常響應
異常響應是指HTTP狀態碼或500的響應狀態,它與正常的響應過程(HTTP狀態碼爲200的響應過程)是一樣的,只是HTTP狀態碼有所不同,因此使用函數render作爲響應過程,並且設置參數status的狀態碼(404或500)即可實現異常響應。
同一個網站的每種異常響應所返回的頁面都是相同的,因此網站的異常響應必須適用於整個項目的所有應用。而在Django中配置全局的異常響應,必須在項目名的urls.py文件配置。以MyDjango爲例,在MyDjango文件夾的urls.py中定義路由以及在index文件夾的views.py中定義視圖函數,代碼如下:
# MyDjango的urls.py
from django.urls import path, include
urlpatterns = [
# 指向index的路由文件urls.py
path('', include(('index.urls', 'index'), namespace='index')),
]
# 全局404頁面配置
handler404 = 'index.views.pag_not_found'
# 全局500頁面配置
handler500 = 'index.views.page_error'
# index的views.py
from django.shortcuts import render
def pag_not_found(request):
"""全局404的配置函數 """
return render(request, '404.html', status=404)
def page_error(request):
"""全局500的配置函數 """
return render(request, '500.html', status=500)
在MyDjango文件夾的urls.py裏設置handler404和handler500,分別指向index文件夾的view.py的視圖函數pag_not_found和page_error。當用戶請求不合理或服務器發生異常時,Django就會根據請求信息執行相應的異常響應。視圖函數分別使用404.html和500.html,因此在templates文件夾裏新增404.html和500.html,代碼如下:
# template文件夾下的404.html和500.html
# 404.html
<!DOCTYPE html>
<html>
<body>
<h3>這是404頁面</h3>
</body>
</html>
# 500.html
<!DOCTYPE html>
<html>
<body>
<h3>這是500頁面</h3>
</body>
</html>
上述內容是設置Django全局404和500的異常響應,只需在項目的urls.py中設置變量handler404和handler500。變量值是指向某個項目應用的視圖函數,而被指向的視圖函數需要設置相應的模板文件和響應狀態碼。
1.4 文件下載功能
響應內容除了返回網頁信息外,還可以實現文件下載功能,是網站常用的功能之一。Django提供三種方式實現文件下載功能,分別是HttpResponse、StreamingHttpResponse和FileResponse,三者說明如下:
- HttpResponse是所有響應過程的核心類,它的底層功能類是HttpResponseBase.
- StreamingHttpResponse是在HttpResponseBase的基礎上進行繼承與重寫的,它實現流式響應輸出(流式響應輸出是使用Python的迭代器將數據進行分段處理並傳輸的),適用於大規模數據響應和文件傳輸響應。
- FileResponse是在StreamingHttpResponse的基礎上進行繼承與重寫的,它實現文件的流式響應輸出,只適用於文件傳輸響應。
以MyDjango爲例,在MyDjango的urls.py、index的urls.py、view.py和templates的index.html中分別定義路由、視圖函數和模板文件,代碼如下:
# MyDjango的urls.py
from django.urls import path, include
urlpatterns = [
# 指向index的路由文件urls.py
path('', include(('index.urls', 'index'), namespace='index')),
]
# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
# 定義首頁的路由
path('', views.index, name='index'),
path('download/file1', views.download1, name='download1'),
path('download/file2', views.download2, name='download2'),
path('download/file3', views.download3, name='download3'),
]
# index的views.py
from django.shortcuts import render
from django.http import HttpResponse, Http404
from django.http import StreamingHttpResponse
from django.http import FileResponse
def index(request):
return render(request, 'index.html')
def download1(request):
file_path = 'D:\cat.jpg'
try:
r = HttpResponse(open(file_path, 'rb'))
r['content_type'] = 'application/octet-stream'
r['Content-Disposition'] = 'attachment; filename=cat.jpg'
return r
except Exception:
raise Http404('Download error')
def download2(request):
file_path = 'D:\duck.jpg'
try:
r = StreamingHttpResponse(open(file_path, 'rb'))
r['content_type'] = 'application/octet-stream'
r['Content-Disposition'] = 'attachment; filename=duck.jpg'
return r
except Exception:
raise Http404('Download error')
def download3(request):
file_path = 'D:\dog.jpg'
try:
f = open(file_path, 'rb')
r = FileResponse(f, as_attachment=True, filename='dog.jpg')
return r
except Exception:
raise Http404('Download error')
# templates的index.html
<!DOCTYPE html>
<html>
<body>
<a href="{% url 'index:download1' %}">HttpResponse-下載</a>
<br>
<a href="{% url 'index:download2' %}">StreamingHttpResponse-下載</a>
<br>
<a href="{% url 'index:download3' %}">FileResponse-下載</a>
</body>
</html>
上述代碼是整個MyDjango項目的功能代碼,文件下載功能實現原理如下:
- MyDjango的urls.py定義的路由指向index的urls.py文件。
- index的urls.py定義4條路由信息,路由index是網站事業,路由所對應的視圖函數index將模板文件index.html作爲網頁內容呈現在瀏覽器上。
- 當在瀏覽器訪問127.0.0.1:8000時,網頁會出現3條地址鏈接,每條地址鏈接分別對應路由download1、download2和downloads3,這些路由所對應的視圖函數分別使用不同的響應類實現文件下載功能。
- 視圖函數download1使用HttpResponse實現文件下載傳入響應類HttpResponse進行實例化,並對實例化對象r設置參數content_type和Content-Disposition,這樣就能實現文件下載功能。
- 視圖函數download2使用StrieamingHttpResponses實現文件下載,該類的使用方式與響應類HttpResponse的使用方式相同。
- 視圖函數download3使用FileResponse實現文件下載,該類的使用方式最爲簡單,只需將文件以字節流的方式讀取並且設置參數as_attachment和filename,然後將三者一併傳入FileResponse進行實例化即可。
上述例子說明三者之間存在一定差異,說明如下:
- HttpResponse實現文件下載存在很大弊端,其工作原理是將文件讀取並載入內存,然後輸出到瀏覽器上實現下載功能。如果下載的文件較大,該方法就會佔用很多內存。對於下載文件,Django推薦使用StreamingHttpResponse和FileResponse方法,這兩個方法將下載文件分批寫入服務器的本地磁盤,而不再將文件載入服務器的內存。
- StreamingHttpResponse和FileResponse的實現原理是相同的,兩者都是將下載文件分批寫入本地磁盤,實現文件的流式響應輸出。
- 從適用範圍來說,StreamingHttpResponse的適用範圍更爲廣泛,可支持大規模數據或文件輸出,而FileResponse只支持文件輸出。
- 從使用方式來說,由於StreamingHttpResponse支持數據或文件輸出,因此在使用時需要設置響應輸出類型和方式,而FileResponse只需設置3個參數即可實現文件下載功能。
2、HTTP請求對象
網站時根據用戶請求來輸出相應的響應內容,用戶請求是指用戶在瀏覽器上訪問某個網址鏈接操作,瀏覽器會根據網址鏈接信息向網站發送HTTP請求,那麼,當Django接收到用戶請求時,它是如何獲取用戶請求信息的呢?
2.1 獲取請求信息
當在瀏覽器上訪問某個網址時,其實質時向網站發送一個HTTP請求,HTTP請求分爲8種請求方式,每種請求方式的說明如下:
請求方式 | 說明 |
---|---|
OPTIONS | 返回服務器針對特定資源所支持的請求方法 |
GET | 向特定資源發出請求(訪問網頁) |
POST | 向指定資源提交數據處理請求(提交表單、上傳文件) |
PUT | 向指定資源位置上傳數據內容跟 |
DELETE | 請求服務器刪除request-URL所標示的資源 |
HEAD | 與GET請求類似,返回的響應中沒有具體內容,用於獲取報頭 |
TRACE | 回覆和顯示服務器收到的請求,用於測試和診斷 |
CONNECT | HTTP/1.1協議中能夠將連接改爲管道方式的代理服務器 |
在上述的HTTP的請求方式裏,最基本的時GET請求和POST請求,網站開發者關心的也只有GET請求和POST請求。GET請求和POST請求是可以設置請求參數的,兩者的設置方式如下:
- GET請求的請求參數是在路由地址後添加"?“和參數內容,參數內容以key=value形式表示,等號前面的是參數名,後面是參數值,如果涉及多個參數,每個參數之間舊使用”&"隔開,如127.0.0.1:8000/?user=xy&pw=123.
- POST請求的請求參數一般以表單的形式傳遞,常見的額表單使用HTML的form標籤,並且form標籤的method屬性設爲POST。
2.2 文件上傳功能
文件上傳功能是網站開發常見的功能之一,比如上傳圖片和導入(音頻文件、辦公文件或安裝包等)。無論上傳的文件是什麼格式的,其上傳原理都是將文件二進制的數據格式讀取並寫入網站指定的文件夾裏。通過一個簡單的例子來使用Django實現文件上傳功能。
# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
# 定義路由
path('', views.upload, name='uploaded'),
]
# index的views.py
from django.shortcuts import render
from django.http import HttpResponse
import os
def upload(request):
# 請求方法爲POST時,執行文件上存
if request.method == "POST":
# 獲取上傳的文件,如果沒有文件,則默認爲None
myFile = request.FILES.get("myfile", None)
if not myFile:
return HttpResponse("no files for upload!")
# 打開特定的文件進行二進制的寫操作
f = open(os.path.join("D:\\upload", myFile.name), 'wb+')
# 分塊寫入文件
for chunk in myFile.chunks():
f.write(chunk)
f.close()
return HttpResponse("upload over!")
else:
# 請求方法爲GET時,生成文件上存頁面
return render(request, 'upload.html')
# templates的upload.html
<!DOCTYPE html>
<html>
<body>
<form enctype="multipart/form-data" action="" method="post">
{% csrf_token %}
<input type="file" name="myfile" />
<br>
<input type="submit" value="上存文件"/>
</form>
</body>
</html>
從視圖函數upload可以看到,如果當前HTTP請求爲POST,就會觸發文件上傳功能。其運行過程如下:
- 模板文件upload.html使用from標籤的文件控件file生成文件上傳功能,該控件將用戶上傳的文件以二進制讀取,讀取方式由form標籤的屬性enctype="multipart/form-data"設置。
- 瀏覽器將用戶上傳的文件讀取後,通過HTTP的POST請求將二進制數據傳到Django,當Django收到POST請求後,從請求對象的屬性FILES獲取文件信息,然後再D盤的upload文件夾裏創建新的文件,文件名(從文件信息對象myFile.name獲取)與用戶上傳的文件名相同。
- 從文件信息對象myFile.chunks()讀取文件內容,並寫入upload文件夾的文件中,從而實現文件上傳功能。