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可以引入面向對象的思想對數據進行更高程度的封裝,此處簡單了解即可,我們將在后續的項目中詳細介紹它的應用於強大之處