Django 系列部落格(七)
Django 系列部落格(七)
前言
本篇部落格介紹 Django 中的檢視層中的相關引數,HttpRequest 物件、HttpResponse 物件、JsonResponse,以及檢視層的兩種響應方式 CBV 和 FBV,還有簡單的檔案上傳。
檢視函式
一個檢視函式,簡稱檢視,是一個簡單的Python 函式,它接受Web請求並且返回Web響應。響應可以是一張網頁的HTML內容,一個重定向,一個404錯誤,一個XML文件,或者一張圖片. . . 是任何東西都可以。無論檢視本身包含什麼邏輯,都要返回響應。程式碼寫在哪裡也無所謂,只要它在你的Python目錄下面。除此之外沒有更多的要求了——可以說“沒有什麼神奇的地方”。為了將程式碼放在某處,約定是將檢視放置在專案或應用程式目錄中的名為views.py
下面是一個返回當前日期和時間作為 HTML 文件的檢視:
from django.shortcuts import render, HttpResponse, HttpResponseRedirect, redirect import datetime def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
這段程式碼解析:
- 從
django.shortcuts
模組匯入了HttpResponse
類,以及Python的datetime
庫; - 定義了
current_datetime
函式。它就是檢視函式。每個檢視函式都使用HttpRequest
物件作為第一個引數,並且通常稱之為request
;
注意,檢視函式的名稱並不重要;不需要用一個統一的命名方式來命名,以便讓Django識別它。我們將其命名為current_datetime
,是因為這個名稱能夠精確地反映出它的功能。
- 會返回一個
HttpResponse
物件,其中包含生成的響應。每個檢視函式都負責返回一個HttpResponse
在檢視層最重要的就是要熟悉兩個物件:請求物件(request)和響應物件(HttpResponse)。
HttpRequest 物件
request 屬性
Django 將請求報文中的請求行、請求頭、請求體封裝成 HttpRequest 類中的屬性。除了特殊說明之外,其他的均為只讀屬性。
1.HttpRequest.GET
一個類似於字典的物件,包含 HTTP GET 的所有引數。詳情請參考 QueryDict 物件。
2.HttpRequest.POST
一個類似於字典的物件,如果請求中包含表單資料,則將這些資料封裝成 QueryDict 物件。
POST 請求可以帶有空的 POST 字典 —— 如果通過 HTTP POST 方法傳送一個表單,但是表單中沒有任何的資料,QueryDict 物件依然會被建立。
因此,不應該使用 if request.POST 來檢查使用的是否是POST 方法;應該使用 if request.method == "POST"
另外:如果使用 POST 上傳檔案的話,檔案資訊將包含在 FILES 屬性中。
注意:鍵值對的值是多個的時候,比如checkbox型別的input標籤,select標籤,需要用:
request.POST.getlist("hobby")
3.HttpRequest.body
一個字串,代表請求報文的主體。在處理非 HTTP 形式的報文時非常有用,例如:二進位制圖片、XML,Json等。
但是,如果要處理表單資料的時候,推薦還是使用 HttpRequest.POST 。
4.HttpRequest.path
一個字串,表示請求的路徑元件(不含域名)。
例如:"/music/bands/the_beatles/"
5.HttpRequest.method
一個字串,表示請求使用的HTTP 方法。必須使用大寫。
例如:"GET"、"POST"
6.HttpRequest.encoding
一個字串,表示提交的資料的編碼方式(如果為 None 則表示使用 DEFAULT_CHARSET 的設定,預設為 'utf-8')。
這個屬性是可寫的,你可以修改它來修改訪問表單資料使用的編碼。
接下來對屬性的任何訪問(例如從 GET 或 POST 中讀取資料)將使用新的 encoding 值。
如果你知道表單資料的編碼不是 DEFAULT_CHARSET ,則使用它。
7.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 鍵。
8.HttpRequest.FILES
一個類似於字典的物件,包含所有的上傳檔案資訊。
FILES 中的每個鍵為<input type="file" name="" /> 中的name,值則為對應的資料。
注意,FILES 只有在請求的方法為POST 且提交的<form> 帶有enctype="multipart/form-data" 的情況下才會
包含資料。否則,FILES 將為一個空的類似於字典的物件。
9.HttpRequest.COOKIES
一個標準的Python 字典,包含所有的cookie。鍵和值都為字串。
10.HttpRequest.session
一個既可讀又可寫的類似於字典的物件,表示當前的會話。只有當Django 啟用會話的支援時才可用。
完整的細節參見會話的文件。
11.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。
request常用方法
1.HttpRequest.get_full_path()
返回 path,如果可以將加上查詢字串。
例如:"/music/bands/the_beatles/?print=true"
注意和path的區別:http://127.0.0.1:8001/order/?name=lqz&age=10
2.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') 裝飾你的檢視以讓響應能夠正確地快取。
HttpResponse 物件
響應物件主要有三種形式:前面的部落格有相關介紹
- HttpResponse();
- render();
- redirect().
HttpResponse()括號內直接跟一個具體的字串作為響應體,比較直接和簡單,所以這裡介紹後兩種
render()
render(request, template_name[, context])
結合給定的模板和一個上下文字典,返回一個經過 Django渲染後的HttpResponse 物件到前端。
引數:
- request:用於生成響應的請求物件;
- template_name:要使用的模板的完整名稱,可選的引數;
- context:新增到模板上下文的一個字典。預設是一個空字典,如果字典中的某個值是可呼叫的,檢視將在渲染模板之前呼叫它;
render 方法就是將一個模板頁面中的模板語法進行渲染,最終渲染成一個 HTML 頁面作為響應返回給前端。
redirect()
傳遞要重定向的一個硬編碼的URL
def my_view(request):
...
return redirect('/some/url/')
也可以是一個完成的 url
def my_view(request):
...
return redirect('http://www.baidu.com/')
JsonResponse物件
向前端返回一個 json 格式的字串,有兩種方式:
第一種方式
import json
data = {'name': 'musibii', 'age': 18}
return HttpResponse(json.dumps(data))
# 從這裡可以看出其實 Jsonresponse內部也是呼叫了 json 的 dumps 方法進行轉換
第二種方式
from django.http import JsonResponse
data = {'name': 'musibii', 'age': 18}
return JsonResponse(data1,safe=False)
# safe引數是一種安全機制,因為如果要傳輸列表型別的資料時,會因為內部的相關機制會產生錯誤。
CBV 和 FBV
CBV 基於類的檢視(Class Base View)和 FBV 基於函式的檢視(Function Base View)
CBV
from django.views import View
class Login(View):
def dispatch(self, request, *args, **kwargs):
print(request)
print(args)
print(kwargs)
# 可以寫類似裝飾器的東西,在前後加程式碼
obj=super().dispatch(request, *args, **kwargs)
return obj
def get(self, request):
return render(request, 'login.html')
def post(self, request):
name = request.POST.get('name')
ret = BookInfo.objects.filter(name=name).first()
print(type(ret))
dic = {}
print(ret.name)
print(type(ret.__dict__))
for key, value in (ret.__dict__).items():
if key[0].startswith('_'):
continue
print(key)
dic[key] = value
# print(dic)
# if ret:
# dic = {}
# for key in ret.items():
# dic[key] = ret
return JsonResponse(dic, json_dumps_params={'ensure_ascii': False})
FBV
def file_upload(request):
if request.method=="GET":
return render(request,'file_upload.html')
else:
print(request.POST)
print(request.FILES)
# print(request.body)
# FILES是一個字典,
# <MultiValueDict: {'myfile': [<InMemoryUploadedFile: 1.jpg (image/jpeg)>]}>
# 拿到上傳的檔案物件
file=request.FILES.get('myfile')
print(type(file))
from django.core.files.uploadedfile import InMemoryUploadedFile
with open(file.name,'wb') as f:
# for line in file.chunks():
for line in file:
f.write(line)
return HttpResponse('上傳成功')
簡單檔案上傳
模板檔案
# upload_file.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
<p>使用者名稱:<input type="text" name="name"></p>
<input type="file" name="myfile">
{# <input type="file" name="myfile2">#}
<input type="submit" value="提交">
</form>
</body>
</html>
路由檔案
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^upload_file/$', views.UploadFile.as_view()),
]
檢視檔案
class UploadFile(View):
def get(self, request):
return render(request, 'upload_file.html')
def post(self, request):
file = request.FILES.get('myfile')
# print(file['file'])
from django.core.files.uploadedfile import InMemoryUploadedFile
print(time.time())
filename = str(time.time()).split('.')[0] + file.name
with open(filename, 'wb') as f:
for line in file:
f.write(line)
return HttpResponse('上傳成功')