Django之視圖層
一 視圖函數
視圖函數,簡稱視圖,屬于Django的視圖層,預設定義在views.py檔案中,是用來處理web請求資訊以及傳回響應資訊的函數,是以研究視圖函數隻需熟練掌握兩個對象即可:請求對象(HttpRequest)和響應對象(HttpResponse)
二 請求對象(HttpRequest)
django将http協定請求封包中的請求行、首部資訊、内容主體封裝到了HttpRequest對象中(類似于我們自定義架構的environ參數)。 django會将HttpRequest對象當做參數傳給視圖函數的第一個參數request,在視圖函數中,通過通路該對象的屬性便可以提取http協定的請求資料
####2.1、HttpRequest對象常用屬性part1
一.HttpRequest.method
擷取請求使用的方法(值為純大寫的字元串格式)。例如:"GET"、"POST"
應該通過該屬性的值來判斷請求方法
二.HttpRequest.GET
值為一個類似于字典的QueryDict對象,封裝了GET請求的所有參數,可通過HttpRequest.GET.get('鍵')擷取相對應的值
三.HttpRequest.POST
值為一個類似于字典的QueryDict對象,封裝了POST請求所包含的表單資料,可通過HttpRequest.POST.get('鍵')擷取相對應的值
針對表單中checkbox類型的input标簽、select标簽送出的資料,鍵對應的值為多個,需要用:HttpRequest.POST.getlist("hobbies")擷取存有多個值的清單,同理也有HttpRequest.GET.getlist("鍵")
案例:
urls.py
from django.urls import re_path
from app01 import views
urlpatterns = [
re_path(r'^login/$',views.login),
]
Views.py
from django.shortcuts import render,HttpResponse
def login(request):
if request.method == 'GET':
# 當請求url為:http://127.0.0.1:8001/login/?a=1&b=2&c=3&c=4&c=5
# 請求方法是GET,?後的請求參數都存放于request.GET中
print(request.GET)
# 輸出<QueryDict: {'a': ['1'], 'b': ['2'], 'c': ['3', '4', '5']}>
# 擷取?後參數的方式為
a=request.GET.get('a') # 1
b=request.GET.get('b') # 2
c=request.GET.getlist('c') # ['3', '4', '5']
return render(request,'login.html')
elif request.method == 'POST':
# 在輸入框内輸入使用者名egon、年齡18,選擇愛好,點選送出
# 請求方法為POST,表單内的資料都會存放于request.POST中
print(request.POST)
# 輸出<QueryDict: {..., 'name': ['egon'], 'age': ['18'], 'hobbies': ['music', 'read']}>
# 擷取表單中資料的方式為
name=request.POST.get('name') # egon
age=request.POST.get('age') # 18
hobbies=request.POST.getlist('hobbies') # ['music', 'read']
return HttpResponse('送出成功')
在templates目錄下建立login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登入頁面</title>
</head>
<body>
<!--
method="post"代表在送出表單時會以POST方法送出表單資料
action="/login/" 代表表單資料的送出位址為http://127.0.0.1:8001/login/,可以簡寫為action="/login/",或者action=""
-->
<form action="http://127.0.0.1:8001/login/" method="post">
{% csrf_token %} <!--強調:必須加上這一行,後續我們會詳細介紹-->
<p>使用者名:<input type="text" name="name"></p>
<p>年齡:<input type="text" name="age"></p>
<p>
愛好:
<input type="checkbox" name="hobbies" value="music">音樂
<input type="checkbox" name="hobbies" value="read">閱讀
<input type="checkbox" name="hobbies" value="dancing">跳舞
</p>
<p><input type="submit" value="送出"></p>
</form>
</body>
</html>
####2.2、HttpRequest對象常用屬性part2
一.HttpRequest.body
當浏覽器基于http協定的POST方法送出資料時,資料會被放到請求體中發送給django,django會将接收到的請求體資料存放于HttpRequest.body屬性中,因為該屬性的值為Bytes類型,是以通常情況下直接處理Bytes、并從中提取有用資料的操作是複雜而繁瑣的,好在django會對它做進一步的處理與封裝以便我們更為友善地提取資料,比如
對于form表單來說,送出資料的常用方法為GET與POST
1:如果表單屬性method='GET',那麼在送出表單時,表單内資料不會存放于請求體中,而是會将表單資料按照k1=v1&k2=v2&k3=v3的格式放到url中,然後發送給django,django會将這些資料封裝到request.GET中,注意此時的request.body為空、無用
2:如果表單屬性method='POST',那麼在送出表單時,表單内的所有資料都會存放于請求體中,在發送給django後會封裝到request.body裡,此時django為了友善我們提取資料,會request.body的資料進行進一步的處理,具體如何處理呢,需要從form表單送出資料的編碼格式說起:
form表單對送出的表單資料有兩種常用的編碼格式,可以通過屬性enctype進行設定,如下
編碼格式1(預設的編碼格式):enctype="application/x-www-form-urlencoded"
編碼格式2(使用form表單上傳檔案時隻能用該編碼):enctype="multipart/form-data"
如果form表單送出資料是按照編碼格式1,那麼request.body中資料的格式類似于GET方法的資料格式,如k1=v1&k2=v2,此時django會将request.body中的資料提取出來封裝到request.POST中友善我們提取
如果form表單送出資料是按照編碼格式2,那麼request.body中資料的格式為b'------WebKitFormBoundaryKtcwuksQltpNprep\r\nContent-Disposition: form-data;......',,此時django會将request.body中的資料提取出來封裝到request.POST中,将上傳的檔案資料專門提取出來封裝到request.FILES屬性中
強調:毫無疑問,編碼格式2的資料量要大于編碼格式1,如果無需上傳檔案,還是推薦使用更為精簡的編碼格式1
我們除了可以采用form表單向django送出資料外,還可以采用ajax技術,ajax可以送出的資料格式有:1、編碼格式1 2、編碼格式2 3、json,當ajax采用POST方法送出前兩種格式的資料時,django的處理方案同上,但是當ajax采用POST方法送出json格式的資料時,django會将接收到的資料存放于HttpRequest.body,此時需要我們自己對HttpRequest.body屬性值做反序列化操作,
具體的,我們在講解ajax時再做具體介紹
二.HttpRequest.FILES
如果使用form表單POST上傳檔案的話,檔案資料将包含在HttpRequest.FILES屬性中。
該屬性值為一個類似于字典的對象,可以包含多組key:value(對應多個上傳的檔案),其中每個key為<input type="file" name="" /> 中name屬性的值,而value則為對應的檔案資料
強調:HttpRequest.FILES 隻有在請求的方法為POST 且送出的<form> 帶有enctype="multipart/form-data" 的情況下才會包含資料。否則,FILES 将為一個空的類似于字典的對象。
案例:form表單上傳檔案
urls.py
from django.urls import path,register_converter,re_path
from app01 import views
urlpatterns = [
re_path(r'^register/$',views.register),
]
views.py
from django.shortcuts import render,HttpResponse
def register(request):
if request.method == 'GET':
return render(request,'register.html')
elif request.method == 'POST':
print(request.body)
# 從request.POST中擷取使用者名
name=request.POST.get('name')
# 從request.FILES擷取檔案對象
file_obj=request.FILES.get('header_img')
# 上傳的檔案存放于templates檔案夾下
with open('templates/header.png','wb') as f:
for line in file_obj:
f.write(line)
return HttpResponse('注冊成功')
在templates目錄下建立register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注冊頁面</title>
</head>
<body>
<form action="" method="POST" enctype="multipart/form-data" >
{% csrf_token %}
<p>
使用者名:<input type="text" name="name">
</p>
<p>
頭像:<input type="file" name="header_img">
</p>
<p>
<input type="submit" value="送出">
</p>
</form>
</body>
</html>
####2.3、HttpRequest對象常用屬性part3
一.HttpRequest.path
擷取url位址的路徑部分,隻包含路徑部分
二.HttpRequest.get_full_path()
擷取url位址的完整path,既包含路徑又包含參數部分
如果請求位址是http://127.0.0.1:8001/order/?name=egon&age=10#_label3,
HttpRequest.path的值為"/order/"
HttpRequest.get_full_path()的值為"/order/?name=egon&age=10"
案例:
urls.py
from django.urls import path,register_converter,re_path
from app01 import views
urlpatterns = [
re_path(r'^order',views.order),
]
views.py
from django.shortcuts import render,HttpResponse
# 針對請求的url位址:http://127.0.0.1:8001/order/?name=egon&age=10#_label3
# 從域名後的最後一個“/”開始到“?”為止是路徑部分,即/order/
# 從“?”開始到“#”為止之間的部分為參數部分,即name=egon&age=10
def order(request):
print(request.path) # 結果為“/order/”
print(request.get_full_path()) # 結果為"/order/?name=egon&age=10"
return HttpResponse('order page')
####2.4、HttpRequest對象常用屬性part4
一.HttpRequest.META
值為包含了HTTP協定的請求頭資料的Python字典,字典中的key及期對應值的解釋如下
CONTENT_LENGTH —— 請求的正文的長度(是一個字元串)。
CONTENT_TYPE —— 請求的正文的MIME類型。
HTTP_ACCEPT —— 響應可接收的Content-Type。
HTTP_ACCEPT_ENCODING —— 響應可接收的編碼。
HTTP_ACCEPT_LANGUAGE —— 響應可接收的語言。
HTTP_HOST —— 客服端發送資料的目标主機與端口
HTTP_REFERER —— Referring 頁面。
HTTP_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 的鍵時,
都會
1、将所有字母大寫
2、将單詞的連接配接符替換為下劃線
3、加上字首HTTP_。
是以,一個叫做 X-Bender 的頭部将轉換成 META 中的 HTTP_X_BENDER 鍵。
注意:下述常用屬性暫且了解即可,待我們講到專門的知識點時再專門詳細講解
二.HttpRequest.COOKIES
一個标準的Python 字典,包含所有的cookie。鍵和值都為字元串。
三.HttpRequest.session
一個既可讀又可寫的類似于字典的對象,表示目前的會話。隻有當Django 啟用會話的支援時才可用。
11.HttpRequest.user(使用者認證元件下使用)
一個 AUTH_USER_MODEL 類型的對象,表示目前登入的使用者。
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)
響應可以是任何形式的内容,比如一個HTML檔案的内容,一個重定向,一個404錯誤,一個XML文檔,或者一張圖檔等。總之,無論視圖本身包含什麼邏輯,都要傳回響應,具體的說,響應對象主要有三種形式:HttpResponse,render,redirect
from django.shortcuts import HttpResponse,render,redirect
###3.1、HttpResponse()
括号内直接跟一個具體的字元串作為響應體,比較直接很簡單,是以這裡主要介紹後面兩種形式。
####3.2、render()
render(request, template_name[, context])
參數:
1、request:用于生成響應的請求對象,固定必須傳入的第一個參數
2、template_name:要使用的模闆的完整名稱,必須傳入,render預設會去templates目錄下查找模闆檔案
3、context:可選參數,可以傳入一個字典用來替換子產品檔案中的變量
綜上,render的功能可以總結為:根據給定字典渲染模闆檔案,并傳回一個渲染後的 HttpResponse對象。
####3.3、redirect()
# 傳回重定向資訊
def my_view(request):
...
return redirect('/some/url/')
# 重定向的位址也可以是一個完整的URL:
def my_view(request):
...
return redirect('http://www.baidu.com/')
重定向轉态碼301與302的差別(了解)
一、301和302的異同。
1、相同之處:
301和302狀态碼都表示重定向,具體點說就是浏覽器在拿到伺服器傳回的這個狀态碼後會自動跳轉到一個新的URL位址(浏覽器會從響應頭Location中擷取新位址),使用者看到的效果都是輸入位址A後瞬間跳轉到了另一個位址B
2、不同之處:
301表示舊位址A的資源已經被永久地移除了,即這個資源不可通路了。搜尋引擎在抓取新内容的同時也将舊的網址轉換為重定向之後的位址;
302表示舊位址A的資源還在,即這個資源仍然可以通路,這個重定向隻是臨時地從舊位址A跳轉到位址B,搜尋引擎會抓取新的内容、并且會儲存舊的網址。 從SEO層面考慮,302要好于301
二、重定向原因:
1、網站調整(如改變網頁目錄結構);
2、網頁被移到一個新位址;
3、網頁擴充名改變(如應用需要把.php改成.Html或.shtml)。
這種情況下,如果不做重定向,則使用者收藏夾或搜尋引擎資料庫中舊位址隻能讓通路客戶得到一個404頁面錯誤資訊,通路流量白白喪失;再者某些注冊了多個域名的網站,也需要通過重定向讓通路這些域名的使用者自動跳轉到主站點等。
###四 JsonResponse
向前端傳回一個json格式字元串的兩種方式
方式一:
import json
def my_view(request):
data=['egon','kevin']
return HttpResponse(json.dumps(data) )
方式二:
from django.http import JsonResponse
def my_view(request):
data=['egon','kevin']
return JsonResponse(data,safe=False)
#預設safe=True代表隻能序列化字典對象,safe=False代表可以序列化字典以外的對象
###五 FBV和CBV
django的視圖層由兩種形式構成:FBV和CBV
1、FBV基于函數的視圖(Function base view),我們之前一直介紹的都是FBV
2、CBV基于類的視圖(Class base view)
案例:
urls.py
from django.urls import path,register_converter,re_path
from app01 import views
urlpatterns = [
re_path(r'^login/',views.LoginView.as_view()), # 必須調用類下的方法as_view
]
views.py
from django.shortcuts import render,HttpResponse,redirect
from django.views import View
class LoginView(View):
def dispatch(self, request, *args, **kwargs): # 可在該方法内做一些預處理操作
# 當請求url為:http://127.0.0.1:8008/login/會先觸發dispatch的執行
# 如果http協定的請求方法為GET,則調用下述get方法
# 如果http協定的請求方法為POST,則調用下述post方法
obj=super().dispatch(request, *args, **kwargs) # 必須繼承父類的dispatch功能
return obj # 必須傳回obj
def get(self,request):
return render(request,'login.html')
def post(self,request):
name=request.POST.get('name')
pwd=request.POST.get('pwd')
if name == 'egon' and pwd == '123':
res='登入成功'
else:
res='使用者名或密碼錯誤'
return HttpResponse(res)
測試:
python manage.py runserver 8001
# 驗證GET請求:在浏覽器輸入:http://127.0.0.1:8001/login/
# 驗證POST請求:在表單内輸入資料然後送出
采用CBV可以引入面向對象的思想對資料進行更高程度的封裝,此處簡單了解即可,我們将在後續的項目中詳細介紹它的應用于強大之處