原文鏈接:https://www.cnblogs.com/maple-shaw/articles/9285269.html
一個視圖函數(類),簡稱視圖,是一個簡單的Python 函數(類),它接受Web請求並且返回Web響應。
響應可以是一張網頁的HTML內容,一個重定向,一個404錯誤,一個XML文檔,或者一張圖片。
無論視圖本身包含什么邏輯,都要返回響應。代碼寫在哪里也無所謂,只要它在你當前項目目錄下面。除此之外沒有更多的要求了——可以說“沒有什么神奇的地方”。為了將代碼放在某處,大家約定成俗將視圖放置在項目(project)或應用程序(app)目錄中的名為views.py的文件中。
一個簡單的視圖
from django.http 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模塊導入了HttpResponse類,以及Python的datetime庫。
-
接着,我們定義了current_datetime函數。它就是視圖函數。每個視圖函數都使用HttpRequest對象作為第一個參數,並且通常稱之為request。
注意,視圖函數的名稱並不重要;不需要用一個統一的命名方式來命名,以便讓Django識別它。我們將其命名為current_datetime,是因為這個名稱能夠比較准確地反映出它實現的功能。
-
這個視圖會返回一個HttpResponse對象,其中包含生成的響應。每個視圖函數都負責返回一個HttpResponse對象。
Django使用請求和響應對象來通過系統傳遞狀態。
當瀏覽器向服務端請求一個頁面時,Django創建一個HttpRequest對象,該對象包含關於請求的元數據。然后,Django加載相應的視圖,將這個HttpRequest對象作為第一個參數傳遞給視圖函數。
每個視圖負責返回一個HttpResponse對象。
CBV和FBV
CBV :class based views FBV :function based views
我們之前寫過的都是基於函數的view,就叫FBV。還可以把view寫成基於類的。
就拿我們之前寫過的添加班級為例:
FBV版:
# FBV版添加班級 def add_class(request): if request.method == "POST": class_name = request.POST.get("class_name") models.Classes.objects.create(name=class_name) return redirect("/class_list/") return render(request, "add_class.html")
CBV版:
# CBV版添加班級 from django.views import View class AddClass(View): def get(self, request): return render(request, "add_class.html") def post(self, request): class_name = request.POST.get("class_name") models.Classes.objects.create(name=class_name) return redirect("/class_list/")
注意:
使用CBV時,urls.py中也做對應的修改:
# urls.py中 url(r'^add_class/$', views.AddClass.as_view()),
將函數改為類(FBV->CBV)案例,即定義CBV的寫法:
新增出版社函數分get請求和post請求,各自返回不同的內容,邏輯代碼如下

# 新增出版社 def add_publisher(request): error = '' # 對請求方式進行判斷 if request.method == 'POST': # 處理POST請求 # 獲取到出版社的名稱 publisher_name = request.POST.get('publisher_name') # 判斷出版社名稱是否有重復的 if models.Publisher.objects.filter(name=publisher_name): error = '出版社名稱已存在' # 判斷輸入的值是否為空 if not publisher_name: error = '不能輸入為空' if not error: # 使用ORM將數據插入到數據庫中 obj = models.Publisher.objects.create(name=publisher_name) # 跳轉到展示出版社的頁面 return redirect('/publisher_list/') # 返回一個包含form表單的頁面 return render(request, 'add_publisher.html', {'error': error})
1)我們要將函數改成類,這個類不能直接只是我們自己寫,因為需要很多功能,自己寫的然后需要繼承Django的View類。
2)定義的類中寫get和post等方法,需要接收參數request。方法中是各自請求做的操作以及返回的一個響應
3)當請求到來時根據請求方法會執行對應的方法,然后返回響應的內容
4)這樣執行的好處是把各個請求的代碼分開了,是邏輯更清楚
5)如果還有其他的請求,那么繼續寫對應請求的方法。
6)使用CBV:將之前對應的函數寫成對應的類,並且模塊.類.as_views() 執行,這樣就實現了CBV寫法,get請求就走get方法
如果沒有加as_views(),那么報錯:
沒有執行報錯:
如此才是正確的
url(r'^add_publisher/', views.AddPblisher.as_view()),
as_view的流程


class View(object): """ Intentionally simple parent class for all views. Only implements dispatch-by-method and simple sanity checking. """ http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] def __init__(self, **kwargs): """ Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things. """ # Go through keyword arguments, and either save their values to our # instance, or raise an error. for key, value in six.iteritems(kwargs): setattr(self, key, value) @classonlymethod def as_view(cls, **initkwargs): """ Main entry point for a request-response process. """ for key in initkwargs: if key in cls.http_method_names: raise TypeError("You tried to pass in the %s method name as a " "keyword argument to %s(). Don't do that." % (key, cls.__name__)) if not hasattr(cls, key): raise TypeError("%s() received an invalid keyword %r. as_view " "only accepts arguments that are already " "attributes of the class." % (cls.__name__, key)) def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) view.view_class = cls view.view_initkwargs = initkwargs # take name and docstring from class update_wrapper(view, cls, updated=()) # and possible attributes set by decorators # like csrf_exempt from dispatch update_wrapper(view, cls.dispatch, assigned=()) return view
cls指的是當前的類,就是我們在views模塊中定義的類,url中執行了as_view(),由下圖可知它的返回值是view,也就是views.AddPblisher.as_view()執行就是定義了一個view方法並返回的就是這個view方法。
這就是相當於從上到下執行,然后到了這里就執行view函數
下面再看view函數做的
cls()將自己寫的類AddPblisher實例化一個對象,並賦予self。然后這個子函數view中就能以self代替AddPblisher的對象做操作。
view函數中將request賦值給實例變量self.request,這樣就能在CBV方式的類中get或其它方法里使用self.request,從而操作請求對象。而在FBV方式中直接使用request。

class View(object): """ Intentionally simple parent class for all views. Only implements dispatch-by-method and simple sanity checking. """ http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] def __init__(self, **kwargs): """ Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things. """ # Go through keyword arguments, and either save their values to our # instance, or raise an error. for key, value in six.iteritems(kwargs): setattr(self, key, value) @classonlymethod def as_view(cls, **initkwargs): #cls指的是當前的類,就是我們在views模塊中定義的類 """ Main entry point for a request-response process. """ for key in initkwargs: if key in cls.http_method_names: raise TypeError("You tried to pass in the %s method name as a " "keyword argument to %s(). Don't do that." % (key, cls.__name__)) if not hasattr(cls, key): raise TypeError("%s() received an invalid keyword %r. as_view " "only accepts arguments that are already " "attributes of the class." % (cls.__name__, key)) def view(request, *args, **kwargs): self = cls(**initkwargs) # cls()將自己寫的類AddPblisher實例化一個對象,並賦予self。然后在類AddPblisher中就能以self代替AddPblisher的對象做操作。 if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request #在這里request和self.request是一樣的,這樣做是為了在自己創建的類中能用self.的方法 self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) #view返回的是dispatch方法的返回值。 view.view_class = cls view.view_initkwargs = initkwargs # take name and docstring from class update_wrapper(view, cls, updated=()) # and possible attributes set by decorators # like csrf_exempt from dispatch update_wrapper(view, cls.dispatch, assigned=()) return view def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. #http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] if request.method.lower() in self.http_method_names: #如果請求方式變小寫后在上面的列表中,那么讓handler通過反射獲取到你的請求方法,否則讓它 handler = getattr(self, request.method.lower(), self.http_method_not_allowed) #等於 self.http_method_not_allowed else: handler = self.http_method_not_allowed #否則handler等於 self.http_method_not_allowed 方法 return handler(request, *args, **kwargs) #返回handler的執行結果給as_view中的view,有人調用as_view方法是,那么返回的就是view的返回值,view方法的返回值是 #當url中執行了as_view方法時,返回的是view這個實現閉包並封裝了數據的函數地址,url中相當於寫的是view這個函數,那么url路由之后會執行view這個函數。 view執行的返回值就是dispatch執行的返回值。dispatch執行的返回值就是handler函數的返回值。[1]如果瀏覽器的請求方式在我的方式列表中(一般是8個)存在,那么handler就是self對象本身和請求方式通過反射獲取到小寫的請求方式的方法。[2]如果不是那么讓它等於另一個方法。[3]如果handler是【1】,那么get方法首先從我寫的類中找,我們在類中寫了get請求方法,那么handler執行的返回值就是我們get的返回內容。也就是我們在自己創建的類中寫了對應請求的方法,那么就會執行我們寫的對應請求的方法。[4]如果handler是【2】,那么返回的self.http_method_not_allowed是不被允許訪問的方法執行返回結果。即打印405錯誤碼並返回執行不被允許的的返回方法。 def http_method_not_allowed(self, request, *args, **kwargs): logger.warning( 'Method Not Allowed (%s): %s', request.method, request.path, extra={'status_code': 405, 'request': request} ) return http.HttpResponseNotAllowed(self._allowed_methods())
#當url中執行了as_view方法時,返回的是view這個實現閉包並封裝了數據的函數地址,url中相當於寫的是view這個函數,那么url路由之后會執行view這個函數。
view執行的返回值就是dispatch執行的返回值。dispatch執行的返回值就是handler函數的返回值。[1]如果瀏覽器的請求方式在我的方式列表中(一般是8個)存在,那么handler就是self對象本身和請求方式通過反射獲取到小寫的請求方式的方法。[2]如果不是那么讓它等於另一個方法。[3]如果handler是【1】,那么get方法首先從我寫的類中找,我們在類中寫了get請求方法,那么handler執行的返回值就是我們get的返回內容。也就是我們在自己創建的類中寫了對應請求的方法,那么就會執行我們寫的對應請求的方法。[4]如果handler是【2】,那么返回的self.http_method_not_allowed是不被允許訪問的方法執行返回結果。即打印405錯誤碼並返回執行不被允許的的返回方法。
如果get不到也是執行不被允許的方法

base.py class View(object): http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] @classonlymethod def as_view(cls, **initkwargs): def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) view.view_class = cls ................. return view def dispatch(self, request, *args, **kwargs): if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) def http_method_not_allowed(self, request, *args, **kwargs): logger.warning( 'Method Not Allowed (%s): %s', request.method, request.path, extra={'status_code': 405, 'request': request} ) return http.HttpResponseNotAllowed(self._allowed_methods()) 另一個文件:response.py class HttpResponseNotAllowed(HttpResponse): status_code = 405 def __init__(self, permitted_methods, *args, **kwargs): super(HttpResponseNotAllowed, self).__init__(*args, **kwargs) self['Allow'] = ', '.join(permitted_methods) def __repr__(self): return '<%(cls)s [%(methods)s] status_code=%(status_code)d%(content_type)s>' % { 'cls': self.__class__.__name__, 'status_code': self.status_code, 'content_type': self._content_type_for_repr, 'methods': self['Allow'], }



如下,我要在子中重寫源碼中的dispatch()方法。因為dispatch執行的結果就是返回的頁面的內容,2處找這個方法先在1處尋找,找到后給瀏覽器返回ok,那么此時無論什么請求都是返回的ok,不符合我們的需要
我們重寫dispatch這個方法,那么就相當於是需要給這個方法添加功能。函數中裝飾器可以給另外的函數添加功能,那么重寫方法時我們可以super執行父類中的方法並接收返回值,在執行父類中方法的前后做些其它的操作給它添加功能。
如果將super前后換成時間,那么就能計算dispatch執行時間
給視圖加裝飾器
使用裝飾器裝飾FBV
FBV本身就是一個函數,所以和給普通的函數加裝飾器無差:
def wrapper(func): def inner(*args, **kwargs): start_time = time.time() ret = func(*args, **kwargs) end_time = time.time() print("used:", end_time-start_time) return ret return inner # FBV版添加班級 @wrapper def add_class(request): if request.method == "POST": class_name = request.POST.get("class_name") models.Classes.objects.create(name=class_name) return redirect("/class_list/") return render(request, "add_class.html")
使用裝飾器裝飾CBV
類中的方法與獨立函數不完全相同,因此不能直接將函數裝飾器應用於類中的方法 ,我們需要先將其轉換為方法裝飾器。
Django中提供了method_decorator裝飾器用於將函數裝飾器轉換為方法裝飾器。
# CBV版添加班級 from django.views import View from django.utils.decorators import method_decorator class AddClass(View): @method_decorator(wrapper) def get(self, request): return render(request, "add_class.html") def post(self, request): class_name = request.POST.get("class_name") models.Classes.objects.create(name=class_name) return redirect("/class_list/")

# 使用CBV時要注意,請求過來后會先執行dispatch()這個方法,如果需要批量對具體的請求處理方法,如get,post等做一些操作的時候,這里我們可以手動改寫dispatch方法,這個dispatch方法就和在FBV上加裝飾器的效果一樣。 class Login(View): def dispatch(self, request, *args, **kwargs): print('before') obj = super(Login,self).dispatch(request, *args, **kwargs) print('after') return obj def get(self,request): return render(request,'login.html') def post(self,request): print(request.POST.get('user')) return HttpResponse('Login.post')
我們在視圖函數中給類中方法加裝飾器,先導入某個類。然后直接在get等方法前面@方式裝飾器(裝飾器函數名)。如果想要所有請求加上裝飾器,那么重寫dispatch方法並執行父的方法,然后給dispatch方法添加這個裝飾器。
還可以加多個,指定方法名字:
在類中方法添加裝飾器,可以直接
@裝飾器名
def 方法名字(self):
使用和不使用method_decorator的區別如下,主要是不使用時參數第一個是自己寫的類對象,傳到裝飾器的第一個參數是方法中的self,第二個才是 我的request對象;使用時那么裝飾器中第一個參數就是request
因此裝飾器中要使用request對象,那么需要判斷第幾個形參是它:
Request對象和Response對象
當get訪問一個網頁的時候,GET和POST返回的都是這里類型的,
當get請求后面有拼接的內容的時候,
get就有值了
當訪問是這個路徑的時候,打印一下request.path_info 返回用戶訪問路徑,不包括ip和端口
打印body,因為get沒有請全體,是沒有內容的bytes類型。請求行,請求頭,請求體
post請求中打印請求體:print('request.body'),數據會進行url編碼,原始的數據編碼了的
request對象
當一個頁面被請求時,Django就會創建一個包含本次請求原信息的HttpRequest對象。
Django會將這個對象自動傳遞給響應的視圖函數,一般視圖函數約定俗成地使用 request 參數承接這個對象。
請求相關的常用值
- path_info 返回用戶訪問url,不包括域名
- method 請求中使用的HTTP方法的字符串表示,全大寫表示。
- GET 包含所有HTTP GET參數的類字典對象
- POST 包含所有HTTP POST參數的類字典對象
- body 請求體,byte類型 request.POST的數據就是從body里面提取到的
屬性
所有的屬性應該被認為是只讀的,除非另有說明。
0.HttpRequest.scheme
表示請求方案的字符串(通常為http或https),即瀏覽器請求協議http https
print(request,type(request)) # <WSGIRequest: GET '/test/'> <class 'django.core.handlers.wsgi.WSGIRequest'>

屬性: 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 對象。 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 啟用會話的支持時才可用。 完整的細節參見會話的文檔。

{ 'ALLUSERSPROFILE': 'C:\\ProgramData', 'APPDATA': 'C:\\Users\\Administrator\\AppData\\Roaming', 'COMMONPROGRAMFILES': 'C:\\Program Files (x86)\\Common Files', 'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files', 'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files', 'COMPUTERNAME': 'PC-20190328RVNA', 'COMSPEC': 'C:\\Windows\\system32\\cmd.exe', 'DJANGO_SETTINGS_MODULE': 'bookmanager.settings', 'FP_NO_HOST_CHECK': 'NO', 'HOMEDRIVE': 'C:', 'HOMEPATH': '\\Users\\Administrator', 'LOCALAPPDATA': 'C:\\Users\\Administrator\\AppData\\Local', 'LOGONSERVER': '\\\\PC-20190328RVNA', 'NUMBER_OF_PROCESSORS': '4', 'OS': 'Windows_NT', 'PATH': 'C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\軟件安裝\\Git\\cmd;C:\\mysql\\mysql-5.6.44-winx64\\bin;C:\\python3\\Scripts\\;C:\\python3\\', 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC', 'PROCESSOR_ARCHITECTURE': 'x86', 'PROCESSOR_ARCHITEW6432': 'AMD64', 'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 58 Stepping 9, GenuineIntel', 'PROCESSOR_LEVEL': '6', 'PROCESSOR_REVISION': '3a09', 'PROGRAMDATA': 'C:\\ProgramData', 'PROGRAMFILES': 'C:\\Program Files (x86)', 'PROGRAMFILES(X86)': 'C:\\Program Files (x86)', 'PROGRAMW6432': 'C:\\Program Files', 'PSMODULEPATH': 'C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\', 'PUBLIC': 'C:\\Users\\Public', 'PYCHARM_HOSTED': '1', 'PYCHARM_MATPLOTLIB_PORT': '50724', 'PYTHONIOENCODING': 'UTF-8', 'PYTHONPATH': 'C:\\mcw\\bookmanager;C:\\軟件安裝\\PyCharm 2018.3.5\\helpers\\pycharm_matplotlib_backend', 'PYTHONUNBUFFERED': '1', 'SESSIONNAME': 'Console', 'SYSTEMDRIVE': 'C:', 'SYSTEMROOT': 'C:\\Windows', 'TEMP': 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp', 'TMP': 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp', 'USERDOMAIN': 'PC-20190328RVNA', 'USERNAME': 'Administrator', 'USERPROFILE': 'C:\\Users\\Administrator', 'WINDIR': 'C:\\Windows', 'WINDOWS_TRACING_FLAGS': '3', 'WINDOWS_TRACING_LOGFILE': 'C:\\BVTBin\\Tests\\installpackage\\csilogfile.log', 'RUN_MAIN': 'true', 'SERVER_NAME': 'PC-20190328RVNA', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PORT': '8000', 'REMOTE_HOST': '', 'CONTENT_LENGTH': '', 'SCRIPT_NAME': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'SERVER_SOFTWARE': 'WSGIServer/0.2', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/test/', 'QUERY_STRING': '', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'text/plain', 'HTTP_HOST': '127.0.0.1:8000', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_CACHE_CONTROL': 'max-age=0', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9', 'HTTP_COOKIE': 'csrftoken=e17Wb68amZdumsRa67LXuTzmdEjaA2lYLj0OkmjiiR2SKPRXBHoYcB3c929v8csg', 'wsgi.input': < _io.BufferedReader name = 776 > , 'wsgi.errors': < _io.TextIOWrapper name = '<stderr>' mode = 'w' encoding = 'UTF-8' > , 'wsgi.version': (1, 0), 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.multithread': True, 'wsgi.multiprocess': False, 'wsgi.file_wrapper': < class 'wsgiref.util.FileWrapper' > , 'CSRF_COOKIE': 'e17Wb68amZdumsRa67LXuTzmdEjaA2lYLj0OkmjiiR2SKPRXBHoYcB3c929v8csg' }
上傳文件示例

def upload(request): """ 保存上傳文件前,數據需要存放在某個位置。默認當上傳文件小於2.5M時,django會將上傳文件的全部內容讀進內存。從內存讀取一次,寫磁盤一次。 但當上傳文件很大時,django會把上傳文件寫到臨時文件中,然后存放到系統臨時文件夾中。 :param request: :return: """ if request.method == "POST": # 從請求的FILES中獲取上傳文件的文件名,file為頁面上type=files類型input的name屬性值 filename = request.FILES["file"].name # 在項目目錄下新建一個文件 with open(filename, "wb") as f: # 從上傳的文件對象中一點一點讀 for chunk in request.FILES["file"].chunks(): # 寫入本地文件 f.write(chunk) return HttpResponse("上傳OK")
1)post表單中寫一個類型是file的input標簽,起個name,給個button。
瀏覽器上傳一張圖片,查看文件是否獲取到,結果為空
需要修改傳輸數據類型:
默認用的是下面這個:post請求中打印請求體:print('request.body'),數據會進行url編碼,指的就是這個編碼
然后上傳文件:文件在request的FILES屬性中
一個類似於字典的對象,包含所有的上傳文件信息。 FILES 中的每個鍵為<input type="file" name="" /> 中的name,值則為對應的數據。 注意,FILES 只有在請求的方法為POST 且提交的<form> 帶有enctype="multipart/form-data" 的情況下才會 包含數據。否則,FILES 將為一個空的類似於字典的對象。
get獲取到這個文件對象。默認位置在項目根目錄,獲取到f1的值,值.name是文件名
全路徑:如果有拼接的內容也會獲取到。不包含IP端口,但是包含參數
方法

1.HttpRequest.get_host() 根據從HTTP_X_FORWARDED_HOST(如果打開 USE_X_FORWARDED_HOST,默認為False)和 HTTP_HOST 頭部信息返回請求的原始主機。 如果這兩個頭部沒有提供相應的值,則使用SERVER_NAME 和SERVER_PORT,在PEP 3333 中有詳細描述。 USE_X_FORWARDED_HOST:一個布爾值,用於指定是否優先使用 X-Forwarded-Host 首部,僅在代理設置了該首部的情況下,才可以被使用。 例如:"127.0.0.1:8000" 注意:當主機位於多個代理后面時,get_host() 方法將會失敗。除非使用中間件重寫代理的首部。 2.HttpRequest.get_full_path() 返回 path,如果可以將加上查詢字符串。 例如:"/music/bands/the_beatles/?print=true" 3.HttpRequest.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None) 返回簽名過的Cookie 對應的值,如果簽名不再合法則返回django.core.signing.BadSignature。 如果提供 default 參數,將不會引發異常並返回 default 的值。 可選參數salt 可以用來對安全密鑰強力攻擊提供額外的保護。max_age 參數用於檢查Cookie 對應的時間戳以確保Cookie 的時間不會超過max_age 秒。 復制代碼 >>> request.get_signed_cookie('name') 'Tony' >>> request.get_signed_cookie('name', salt='name-salt') 'Tony' # 假設在設置cookie的時候使用的是相同的salt >>> request.get_signed_cookie('non-existing-cookie') ... KeyError: 'non-existing-cookie' # 沒有相應的鍵時觸發異常 >>> request.get_signed_cookie('non-existing-cookie', False) False >>> request.get_signed_cookie('cookie-that-was-tampered-with') ... BadSignature: ... >>> request.get_signed_cookie('name', max_age=60) ... SignatureExpired: Signature age 1677.3839159 > 60 seconds >>> request.get_signed_cookie('name', False, max_age=60) False 復制代碼 4.HttpRequest.is_secure() 如果請求時是安全的,則返回True;即請求通是過 HTTPS 發起的。 5.HttpRequest.is_ajax() 如果請求是通過XMLHttpRequest 發起的,則返回True,方法是檢查 HTTP_X_REQUESTED_WITH 相應的首部是否是字符串'XMLHttpRequest'。 大部分現代的 JavaScript 庫都會發送這個頭部。如果你編寫自己的 XMLHttpRequest 調用(在瀏覽器端),你必須手工設置這個值來讓 is_ajax() 可以工作。 如果一個響應需要根據請求是否是通過AJAX 發起的,並且你正在使用某種形式的緩存例如Django 的 cache middleware, 你應該使用 vary_on_headers('HTTP_X_REQUESTED_WITH') 裝飾你的視圖以讓響應能夠正確地緩存。
注意:鍵值對的值是多個的時候,比如checkbox類型的input標簽,select標簽,需要用:
request.POST.getlist("hobby")
Response對象
與由Django自動創建的HttpRequest對象相比,HttpResponse對象是我們的職責范圍了。我們寫的每個視圖都需要實例化,填充和返回一個HttpResponse。
如果沒有返回報錯,需要返回一個HttpResponse對象
HttpResponse類位於django.http模塊中。
render參數,有request 模板 上下文就是我們傳的變量(字典)
下面render_to_string方法會拿到模板和變量做字符串替換。返回的content就是渲染之后的html字符串。這個content內容又給了HttpResponse對象。也就是說render里面返回的還是HttpResponse對象,只是里面調用其它的方法把這個模板渲染好了。這里是Django的模板引擎,也可以換成jinja2的。
再看看redirect,拿到的是兩個類:,返回的而是其中一個類的對象
下面這個是永久跳轉的類,
永久跳轉的類繼承的重定向基本的類,又是繼承的HttpResponse這個類:location就是我們在開發這個工具中看到的重定向中的那個屬性。所以重定向也是繼承的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")
設置或刪除響應頭信息
response = HttpResponse() response['Content-Type'] = 'text/html; charset=UTF-8' del response['Content-Type']
屬性
HttpResponse.content:響應內容
HttpResponse.charset:響應內容的編碼
HttpResponse.status_code:響應的狀態碼
JsonResponse對象
JsonResponse是HttpResponse的子類,專門用來生成JSON編碼的響應。
from django.http import JsonResponse response = JsonResponse({'foo': 'bar'}) print(response.content) b'{"foo": "bar"}'
默認只能傳遞字典類型,如果要傳遞非字典類型需要設置一下safe關鍵字參數。
response = JsonResponse([1, 2, 3], safe=False)
定義一個字典返回給瀏覽器
顯示的是name和age連到一塊的:將字典迭代拿的里面的key
但是我想要的是完整的字典,那么我就要自己做json序列化了:
顯示的是完整的字典內容
那么還可以使用另一個類做返回內容傳遞字典數據,直接將字典放到JsonResponse對象中
效果如下:跟自己做的json序列化比起來,小了點細了點。並且告訴瀏覽器當前發送數據內容的類型。如果是在使用ajax,那么它遇到這個內容它自己會做反序列化。前端發送一個請求,回來之后它看到這個類型自己會將這個數據反序列化,變成前端使用的對象,然后點的方法取里面的內容。
如果是自己做序列化的話那么響應頭的內容類型如下。發送的是普通文本,它不會給你做任 何變化,你想用的話就需要自己做反序列化
我們也可以自己序列化發送,但是需要修改這個內容類型的屬性,這樣也可以達到一樣的效果。用字典重新賦值的方式修改它的屬性值
HttpResponse默認返回的是text/html charset utf-8 JsonResponse默認返回的是 application/json 。
下面可以找一下它的默認的:
這個settings點不進去,它是全局配置文件里的:由下可以看到HttpResponse返回的是默認類型
由下可以看出沒有傳默認參數那么使用的是text/html,
那么把內容類型傳遞一個參數修改成別的也可以改變它的類型:
由下可以看到JsonResponse默認的就是 application/json 。JsonReponse會給你做序列化,並且響應頭給你設置成application/json,有了這個之后,前端拿到數據就自動反序列化
那么其它類型的比如列表傳輸呢?報錯: In order to allow non-dict objects to be serialized set the safe parameter to False.
那么根據提示設置safe為False前端就拿到數據了。這是因為JsonResponse默認只支持序列化字典類型的。如果是非字典類型的那么就要加上safe=False
Django shortcut functions
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))
redirect()
參數可以是:
- 一個模型:將調用模型的get_absolute_url() 函數
- 一個視圖,可以帶有參數:將使用urlresolvers.reverse 來反向解析名稱
- 一個絕對的或相對的URL,將原封不動的作為重定向的位置。
默認返回一個臨時的重定向;傳遞permanent=True 可以返回一個永久的重定向。
你可以用多種方式使用redirect() 函數。
傳遞一個具體的ORM對象(了解即可)
將調用具體ORM對象的get_absolute_url() 方法來獲取重定向的URL:
from django.shortcuts import redirect def my_view(request): ... object = MyModel.objects.get(...) return redirect(object)
傳遞一個視圖的名稱
def my_view(request): ... return redirect('some-view-name', foo='bar')
傳遞要重定向到的一個具體的網址
def my_view(request): ... return redirect('/some/url/')
當然也可以是一個完整的網址
def my_view(request): ... return redirect('http://example.com/')
默認情況下,redirect() 返回一個臨時重定向。以上所有的形式都接收一個permanent 參數;如果設置為True,將返回一個永久的重定向:
def my_view(request): ... object = MyModel.objects.get(...) return redirect(object, permanent=True)
擴展閱讀:
臨時重定向(響應狀態碼:302)和永久重定向(響應狀態碼:301)對普通用戶來說是沒什么區別的,它主要面向的是搜索引擎的機器人。
A頁面臨時重定向到B頁面,那搜索引擎收錄的就是A頁面。
A頁面永久重定向到B頁面,那搜索引擎收錄的就是B頁面。