Django框架之View.py(視圖文件)
1. 視圖簡介
視圖層是Django處理請求的核心代碼層,我們大多數Python代碼都集中在這一層面。
它對外接收用戶請求,對內調度模型層和模版層,統合數據庫和前端,最后根據業務邏輯,將處理好的數據,與前端結合,返回給用戶。視圖層是真正的后端,是Python工程師的‘主營業務’。
視圖就是每個創建的APP中的的view.py文件夾。我們處理數據的那些業務邏輯,就都存放(編輯)在這里。
Django的視圖層包含下面一些主要內容:
- URL路由
- 視圖函數
- 快捷方式
- 裝飾器
- 請求與響應
- 類視圖
- 文件上傳
- CSV/PDF生成
- 內置中間件
注意:視圖層應該包含視圖文件(view.py)和路由文件(urls.py)兩個系統組成。
一個最新建的Django APP ,新建的APP名稱為(app001)如下:

1.1 一個簡單的視圖
from django.http import HttpResponse
from django.shortcuts import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
讓我們來逐行解釋下上面的代碼:
-
首先,我們從 django.http(或者django.shorcuts)模塊導入了HttpResponse類,以及Python的datetime庫。
索引上面的兩個from引入語句的功能是一樣的,只需要其中一條就可以了!!!!!
-
接着,我們定義了current_datetime函數。它就是視圖函數。
-
每個視圖函數都使用接收到的HttpRequest對象作為第一個參數,並且通常稱之為request。
-
注意,視圖函數的名稱並不重要;不需要用一個統一的命名方式來命名,以便讓Django識別它。我們將其命名為current_datetime,是因為這個名稱能夠比較准確地反映出它實現的功能(唯一性)。
-
這個視圖會返回一個HttpResponse對象,其中包含生成的響應(HTML頁面或者一個字符串)。
-
每個視圖函數都負責返回一個對象,可以是HttpResponse對象,也可以render對象,還可以是redirect對象。
所以,我們可以將上面的from引入語句修改得全一點,如下:
from django.shortcuts import render, HttpResponse, redirect
在平時的開發中,我們也會稱 render, HttpResponse, redirect 這三個為 “ 基礎必配三件套 ” 。
注意,HttpResponse是一個類,而render和redirect是Django的內置函數!
Django使用請求和響應對象來通過系統傳遞狀態。
當瀏覽器向服務端請求一個頁面時,Django創建一個HttpRequest對象,該對象包含關於請求的元數據。然后,Django加載相應的視圖,將這個HttpRequest對象作為第一個參數傳遞給視圖函數。
每個視圖負責返回一個HttpResponse對象給瀏覽器。
2. 視圖函數的前奏(FBV和CBV)
什么是FBV? 在視圖文件里面編輯的時候,基於函數的形式來編輯業務邏輯的視圖view,就叫做FBV (function base view)。
什么是CBV? 而在編輯業務邏輯的時候,基於類的形式來編輯業務邏輯的則是叫做CBV(class base view)。
下面用一個簡單的例子來說明:
使用FBV寫:
from django.shortcuts import render, HttpResponse, redirect def back_hello(request): return HttpResponse('大家好!')
使用CBV寫:
from django.views import View # 基於類的形式來寫試圖上的業務邏輯的時候,定義的類需要 繼承 django 中的View類 !!!!!!!
class BackHello(View): def get(self, request): return HttpResponse("get請求的時候!get大家好!") def post(self, request): return HttpResponse("post請求的時候!post大家好!")
注意:使用CBV形式的時候,urls.py上的路徑與函數的對應關系,是與FBV不一樣的,需要修改,如下:
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^test/', views.BackHello.as_view()), ] # 可以看出,test/位置中的路徑寫法與FBV一樣,時候后面的引入格式變成了:
# views.類名.as_view()。而CBV情況下是寫為: views.函數名 # 注意:as_view(),這個是固定的!!!!
3. Django視圖函數
視圖函數,簡稱視圖,本質上就是一個Python函數,只是他在django的框架下能做一些特殊的相應,比如它可以接受Web請求做出一些邏輯運算,並且返回Web響應。
響應的內容可以是HTML網頁、重定向、404錯誤,XML文檔或圖像等任何東西。但是,無論視圖本身是個什么處理邏輯,必須都要有一個返回某種響應。
通常我們約定將視圖放置在項目或應用程序目錄中的名為views.py的文件中,而視圖函數也是放在視圖文件當中,這也是一種模塊化思維,把各種功能規划好,方便未來的管理。
4. Django內置快捷方法(函數)
Django在django.shortcuts模塊中,為我們提供了很多快捷方便的內置函數,其中有些使用的頻率很高!
4.1 render()
Django關於render的源碼如下:

結合一個給定的模板和一個給定的上下文字典,並返回一個渲染后的 HttpResponse 對象。
參數:
必備(常用)參數: request: 用於生成響應的請求對象。 template_name:要使用的模板的完整名稱,可選的參數 context:添加到模板上下文的一個字典。默認是一個空字典。如果字典中的某個值是可調用的,視圖將在渲染模板之前調用它。
可以選參數: content_type:生成的文檔要使用的MIME類型。默認為 DEFAULT_CONTENT_TYPE 設置的值。默認為'text/html' status:響應的狀態碼。默認為200。
useing: 用於加載模板的模板引擎的名稱。
一個簡單的例子:
from django.shortcuts import render
def my_view(request):
# 視圖的代碼寫在這里
return render(request, 'myapp/index.html', {'foo': 'bar'})
上面的代碼等於:
from django.http import HttpResponse
from django.template import loader
def my_view(request):
# 視圖代碼寫在這里
t = loader.get_template('myapp/index.html')
c = {'foo': 'bar'}
return HttpResponse(t.render(c, request))
4.2 redirect()

參數:參數范圍相對render來說比較廣
- 一個模型:將調用模型的get_absolute_url() 函數
- 一個視圖,可以帶有參數:將使用urlresolvers.reverse 來反向解析名稱
- 一個絕對的或相對的URL,將原封不動的作為重定向的位置。
默認返回一個臨時的重定向;傳遞一個參數名為 permanent=True 就可以返回一個永久的重定向。
redirect使用的兩個場景:其中,第一個場景使用的比較多!!!!
1. 傳遞視圖名,使用reverse()方法反向解析url:
from django.shortcuts import redirect
def my_view(request): ... return redirect('some-view-name', foo='bar')
1.1 重定向到硬編碼的URL:
def my_view(request): ... return redirect('/some/url/')
1.2 重定向到一個完整的URL:
def my_view(request): ... return redirect('https://example.com/')
2. 調用對象的get_absolute_url()方法來重定向URL:
def my_view(request): ... object = MyModel.objects.get(...) return redirect(object)
所有上述形式都接受permanent參數;如果設置為True,將返回永久重定向:
def my_view(request): ... object = MyModel.objects.get(...) return redirect(object, permanent=True)
5. 什么是臨時重定向?永久重定向?
臨時重定向的響應狀態碼:302
永久重定向的響應狀態碼:301
這兩個的狀態碼對於普通的用戶來說是沒有任何區別,因為他們主要針對的是搜索引擎機器人。
這就涉及到了,另外一個領域了,這就是SEO工作人員的必配只是!
A頁面臨時重定向到B頁面,那搜索引擎收錄的就是A頁面。
A頁面永久重定向到B頁面,那搜索引擎收錄的就是B頁面。
6. Django兩個重要對象
6.1 request對象
每當一個用戶請求發送過來,Django將HTTP數據包中的相關內容,打包成為一個HttpRequest對象,並傳遞給每個視圖函數作為第一位置參數,也就是request,供我們調用。
說明為什么別的教程會叫HttpRequst對象?:這里說是request對象是不太嚴謹的,因為我們指的request只是每一個視圖函數中,固定的第一位置的一個參數名稱。至於為什么我們要取名為request,只是一個大家約定熟成而已,再者就是名稱比HttpRequest短,大家更加好記 ~_~ 。所以,真正的應該是對象應該是,HttpRequest類生產的一個對象,所以在別的地方,也會成為HttpRequest對象。
6.1.1 請求相關的常用值
其實也是屬於request的屬性,只是平時使用頻率很高,所以把它放大到前面一些,方便大家學習。
- path_info 返回用戶訪問url,不包括域名
- method 請求中使用的HTTP方法的字符串表示,全大寫表示。
- GET 包含所有HTTP GET參數的類字典對象
- POST 包含所有HTTP POST參數的類字典對象
- body 請求體,byte類型 request.POST的數據就是從body里面提取到的
6.1.2 request對象的屬性:
下面的把request說成HttpRequest,這兩個是沒有區別的哦!!!!!上面也做了解釋了
屬性: django將請求報文中的請求行、頭部信息、內容主體封裝成 HttpRequest 類中的屬性。 除了特殊說明的之外,其他均為只讀的。 0.HttpRequest.scheme 表示請求方案的字符串(通常為http或https) 1.HttpRequest.body 一個字符串,代表請求報文的主體。在處理非 HTTP 形式的報文時非常有用,例如:二進制圖片、XML,Json等。 但是,如果要處理表單數據的時候,推薦還是使用 HttpRequest.POST 。 另外,我們還可以用 python 的類文件方法去操作它,詳情參考 HttpRequest.read() 。 2.HttpRequest.path 一個字符串,表示請求的路徑組件(不含域名)。 例如:"/music/bands/the_beatles/"
3.HttpRequest.method 一個字符串,表示請求使用的HTTP 方法。必須使用大寫。 例如:"GET"、"POST"
4.HttpRequest.encoding 一個字符串,表示提交的數據的編碼方式(如果為 None 則表示使用 DEFAULT_CHARSET 的設置,默認為 'utf-8')。 這個屬性是可寫的,你可以修改它來修改訪問表單數據使用的編碼。 接下來對屬性的任何訪問(例如從 GET 或 POST 中讀取數據)將使用新的 encoding 值。 如果你知道表單數據的編碼不是 DEFAULT_CHARSET ,則使用它。 5.HttpRequest.GET 一個類似於字典的對象,包含 HTTP GET 的所有參數。詳情請參考 QueryDict 對象。點我知道 >>> 什么是QueryDict對象?
6.HttpRequest.POST 一個類似於字典的對象,如果請求中包含表單數據,則將這些數據封裝成 QueryDict 對象。 POST 請求可以帶有空的 POST 字典 —— 如果通過 HTTP POST 方法發送一個表單,但是表單中沒有任何的數據,QueryDict 對象依然會被創建。 因此,不應該使用 if request.POST 來檢查使用的是否是POST 方法;應該使用 if request.method == "POST" 另外:如果使用 POST 上傳文件的話,文件信息將包含在 FILES 屬性中。 7.HttpRequest.COOKIES 一個標准的Python 字典,包含所有的cookie。鍵和值都為字符串。 8.HttpRequest.FILES 一個類似於字典的對象,包含所有的上傳文件信息。 FILES 中的每個鍵為<input type="file" name="" /> 中的name,值則為對應的數據。 注意,FILES 只有在請求的方法為POST 且提交的<form> 帶有enctype="multipart/form-data" 的情況下才會 包含數據。否則,FILES 將為一個空的類似於字典的對象。 9.HttpRequest.META 一個標准的Python 字典,包含所有的HTTP 首部。具體的頭部信息取決於客戶端和服務器,下面是一些示例: CONTENT_LENGTH —— 請求的正文的長度(是一個字符串)。 CONTENT_TYPE —— 請求的正文的MIME 類型。 HTTP_ACCEPT —— 響應可接收的Content-Type。 HTTP_ACCEPT_ENCODING —— 響應可接收的編碼。 HTTP_ACCEPT_LANGUAGE —— 響應可接收的語言。 HTTP_HOST —— 客服端發送的HTTP Host 頭部。 HTTP_REFERER —— Referring 頁面。 HTTP_USER_AGENT —— 客戶端的user-agent 字符串。 QUERY_STRING —— 單個字符串形式的查詢字符串(未解析過的形式)。 REMOTE_ADDR —— 客戶端的IP 地址。 REMOTE_HOST —— 客戶端的主機名。 REMOTE_USER —— 服務器認證后的用戶。 REQUEST_METHOD —— 一個字符串,例如"GET" 或"POST"。 SERVER_NAME —— 服務器的主機名。 SERVER_PORT —— 服務器的端口(是一個字符串)。 從上面可以看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 之外,請求中的任何 HTTP 首部轉換為 META 的鍵時, 都會將所有字母大寫並將連接符替換為下划線最后加上 HTTP_ 前綴。 所以,一個叫做 X-Bender 的頭部將轉換成 META 中的 HTTP_X_BENDER 鍵。 10.HttpRequest.user 一個 AUTH_USER_MODEL 類型的對象,表示當前登錄的用戶。 如果用戶當前沒有登錄,user 將設置為 django.contrib.auth.models.AnonymousUser 的一個實例。你可以通過 is_authenticated() 區分它們。 例如: if request.user.is_authenticated(): # Do something for logged-in users.
else: # Do something for anonymous users.
user 只有當Django 啟用 AuthenticationMiddleware 中間件時才可用。 ------------------------------------------------------------------------------------- 匿名用戶 class models.AnonymousUser django.contrib.auth.models.AnonymousUser 類實現了django.contrib.auth.models.User 接口,但具有下面幾個不同點: id 永遠為None。 username 永遠為空字符串。 get_username() 永遠返回空字符串。 is_staff 和 is_superuser 永遠為False。 is_active 永遠為 False。 groups 和 user_permissions 永遠為空。 is_anonymous() 返回True 而不是False。 is_authenticated() 返回False 而不是True。 set_password()、check_password()、save() 和delete() 引發 NotImplementedError。 New in Django 1.8: 新增 AnonymousUser.get_username() 以更好地模擬 django.contrib.auth.models.User。 11.HttpRequest.session 一個既可讀又可寫的類似於字典的對象,表示當前的會話。只有當Django 啟用會話的支持時才可用。 完整的細節參見會話的文檔。 request屬性相關
示例:正在更新中………………
6.1.2 request對象的方法:
1 1.HttpRequest.get_host() 2 3 根據從HTTP_X_FORWARDED_HOST(如果打開 USE_X_FORWARDED_HOST,默認為False)和 HTTP_HOST 頭部信息返回請求的原始主機。 4 如果這兩個頭部沒有提供相應的值,則使用SERVER_NAME 和SERVER_PORT,在PEP 3333 中有詳細描述。 5 6 USE_X_FORWARDED_HOST:一個布爾值,用於指定是否優先使用 X-Forwarded-Host 首部,僅在代理設置了該首部的情況下,才可以被使用。 7 8 例如:"127.0.0.1:8000" 9 10 注意:當主機位於多個代理后面時,get_host() 方法將會失敗。除非使用中間件重寫代理的首部。 11 12 13 14 2.HttpRequest.get_full_path() 15 16 返回 path,如果可以將加上查詢字符串。 17 18 例如:"/music/bands/the_beatles/?print=true" 19 20 21 22 3.HttpRequest.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None) 23 24 返回簽名過的Cookie 對應的值,如果簽名不再合法則返回django.core.signing.BadSignature。 25 26 如果提供 default 參數,將不會引發異常並返回 default 的值。 27 28 可選參數salt 可以用來對安全密鑰強力攻擊提供額外的保護。max_age 參數用於檢查Cookie 對應的時間戳以確保Cookie 的時間不會超過max_age 秒。 29 30 復制代碼 31 >>> request.get_signed_cookie('name') 32 'Tony' 33 >>> request.get_signed_cookie('name', salt='name-salt') 34 'Tony' # 假設在設置cookie的時候使用的是相同的salt 35 >>> request.get_signed_cookie('non-existing-cookie') 36 ... 37 KeyError: 'non-existing-cookie' # 沒有相應的鍵時觸發異常 38 >>> request.get_signed_cookie('non-existing-cookie', False) 39 False 40 >>> request.get_signed_cookie('cookie-that-was-tampered-with') 41 ... 42 BadSignature: ... 43 >>> request.get_signed_cookie('name', max_age=60) 44 ... 45 SignatureExpired: Signature age 1677.3839159 > 60 seconds 46 >>> request.get_signed_cookie('name', False, max_age=60) 47 False 48 復制代碼 49 50 51 52 4.HttpRequest.is_secure() 53 54 如果請求時是安全的,則返回True;即請求通是過 HTTPS 發起的。 55 56 57 58 5.HttpRequest.is_ajax() 59 60 如果請求是通過XMLHttpRequest 發起的,則返回True,方法是檢查 HTTP_X_REQUESTED_WITH 相應的首部是否是字符串'XMLHttpRequest'。 61 62 大部分現代的 JavaScript 庫都會發送這個頭部。如果你編寫自己的 XMLHttpRequest 調用(在瀏覽器端),你必須手工設置這個值來讓 is_ajax() 可以工作。 63 64 如果一個響應需要根據請求是否是通過AJAX 發起的,並且你正在使用某種形式的緩存例如Django 的 cache middleware, 65 你應該使用 vary_on_headers('HTTP_X_REQUESTED_WITH') 裝飾你的視圖以讓響應能夠正確地緩存。 66 67 請求相關方法
注意:鍵值對的值是多個的時候,比如checkbox類型的input標簽,select標簽,也就是一個標簽可能會有很多值的時候,
需要用下面的getlist方法來獲取多個值:
request.POST.getlist("hobby")
6.2 Response對象
HttpResponse類定義在django.http模塊中。
HttpRequest對象由Django自動創建,而HttpResponse對象則由程序員手動創建.
我們編寫的每個視圖都要實例化、填充和返回一個HttpResponse對象。也就是函數的return值。
6.2.1 使用方法
1. 傳遞一個字符串
最簡單的方式是傳遞一個字符串作為頁面的內容到HttpResponse構造函數,並返回給用戶:
from django.http import HttpResponse response = HttpResponse("Here's the text of the Web page.") response = HttpResponse("Text only, please.", content_type="text/plain")
2. 傳遞可迭代對象
HttpResponse會立即處理這個迭代器,並把它的內容存成字符串,最后廢棄這個迭代器。比如文件在讀取后,會立刻調用close()方法,關閉文件。
3. 設置或刪除響應頭信息
response = HttpResponse() response['Content-Type'] = 'text/html; charset=UTF-8'
del response['Content-Type']
6.2.2 Response對象的屬性
前面三個重點記憶:
1. HttpResponse.content 響應的內容。bytes類型。 2. HttpResponse.charset 編碼的字符集。 如果沒指定,將會從content_type中解析出來。 3. HttpResponse.status_code 響應的狀態碼,比如200。
后面三個盡力而為: 4. HttpResponse.reason_phrase 響應的HTTP原因短語。 使用標准原因短語。 除非明確設置,否則reason_phrase由status_code的值決定。 5. HttpResponse.streaming 這個屬性的值總是False。由於這個屬性的存在,使得中間件能夠區別對待流式響應和常規響應。 6. HttpResponse.closed 如果響應已關閉,那么這個屬性的值為True。
6.3 JsonResponse類對象
JsonResponse是HttpResponse的子類,專門用來生成JSON編碼的響應。
大白話的說:就是JsonResponse類,可以把一些數據類型,比如:字典,列表,元組,集合等數據類型的結構,轉換成為字符串,以便用於轉遞到別的地方,提供別人使用!!!!
例如:有一個字典dict = {"aa":11, "bb":22},此時的dict只是一個字典(數據類型),當把字典dict經過JsonResponse類的內部變換之后,會把dict變成字符串 ” {"aa":11, "bb":22} “,注意字典兩邊的大括號都會一起變成字符串!!!!
from django.http import JsonResponse
response = JsonResponse({'foo': 'bar'})
print(response.content)
b'{"foo": "bar"}'
JsonResponse默認只能傳遞字典類型,如果要傳遞非字典類型需要設置一下safe關鍵字參數。
response = JsonResponse([1, 2, 3], safe=False)
什么是QueryDict對象?
類的原型:class QueryDict[source] 在HttpRequest對象中,GET和POST屬性都是一個django.http.QueryDict的實例。也就是說你可以按本文下面提供的方法操作request.POST和request.GET。 request.POST或request.GET的QueryDict都是不可變,只讀的。如果要修改它,需要使用QueryDict.copy()方法,獲取它的一個拷貝,然后在這個拷貝上進行修改操作。 一、方法 QueryDict 實現了Python字典數據類型的所有標准方法,因為它是字典的子類。 不同之處在於下面: 1. QueryDict.init(query_string=None, mutable=False, encoding=None)[source] QueryDict實例化方法。注意:QueryDict的鍵值是可以重復的! >>> QueryDict('a=1&a=2&c=3') <QueryDict: {'a': ['1', '2'], 'c': ['3']}> 如果需要實例化可以修改的對象,添加參數mutable=True。 2. classmethod QueryDict.fromkeys(iterable, value='', mutable=False, encoding=None)[source] Django1.11中的新功能。 循環可迭代對象中的每個元素作為鍵值,並賦予同樣的值(來至value參數)。 >>> QueryDict.fromkeys(['a', 'a', 'b'], value='val') <QueryDict: {'a': ['val', 'val'], 'b': ['val']}> 3. QueryDict.update(other_dict) 用新的QueryDict或字典更新當前QueryDict。類似dict.update(),但是追加內容,而不是更新並替換它們。 像這樣: >>> q = QueryDict('a=1', mutable=True) >>> q.update({'a': '2'}) >>> q.getlist('a') ['1', '2'] >>> q['a'] # returns the last '2' 4. QueryDict.items() 類似dict.items(),如果有重復項目,返回最近的一個,而不是都返回: >>> q = QueryDict('a=1&a=2&a=3') >>> q.items() [('a', '3')] 5. QueryDict.values() 類似dict.values(),但是只返回最近的值。 像這樣: >>> q = QueryDict('a=1&a=2&a=3') >>> q.values() ['3'] 6. QueryDict.copy()[source] 使用copy.deepcopy()返回QueryDict對象的副本。 此副本是可變的! 7. QueryDict.getlist(key, default=None) 返回鍵對應的值列表。 如果該鍵不存在並且未提供默認值,則返回一個空列表。 8. QueryDict.setlist(key, list_)[source] 為list_設置給定的鍵。 9. QueryDict.appendlist(key, item)[source] 將鍵追加到內部與鍵相關聯的列表中。 10. QueryDict.setdefault(key, default=None)[source] 類似dict.setdefault(),為某個鍵設置默認值。 11. QueryDict.setlistdefault(key, default_list=None)[source] 類似setdefault(),除了它需要的是一個值的列表而不是單個值。 12. QueryDict.lists() 類似items(),只是它將其中的每個鍵的值作為列表放在一起。 像這樣: >>> q = QueryDict('a=1&a=2&a=3') >>> q.lists() [('a', ['1', '2', '3'])] 13. QueryDict.pop(key)[source] 返回給定鍵的值的列表,並從QueryDict中移除該鍵。 如果鍵不存在,將引發KeyError。 像這樣: >>> q = QueryDict('a=1&a=2&a=3', mutable=True) >>> q.pop('a') ['1', '2', '3'] 14. QueryDict.popitem()[source] 刪除QueryDict任意一個鍵,並返回二值元組,包含鍵和鍵的所有值的列表。在一個空的字典上調用時將引發KeyError。 像這樣: >>> q = QueryDict('a=1&a=2&a=3', mutable=True) >>> q.popitem() ('a', ['1', '2', '3']) 15. QueryDict.dict() 將QueryDict轉換為Python的字典數據類型,並返回該字典。 如果出現重復的鍵,則將所有的值打包成一個列表,最為新字典中鍵的值。 >>> q = QueryDict('a=1&a=3&a=5') >>> q.dict() {'a': '5'} 16. QueryDict.urlencode(safe=None)[source] 已url的編碼格式返回數據字符串。 像這樣: >>> q = QueryDict('a=2&b=3&b=5') >>> q.urlencode() 'a=2&b=3&b=5' 使用safe參數傳遞不需要編碼的字符。 像這樣: >>> q = QueryDict(mutable=True) >>> q['next'] = '/a&b/' >>> q.urlencode(safe='/') 'next=/a%26b/'

