本文正在參加「Python主題月」,詳情查看 活動連接html
微信公衆號搜索【程序媛小莊】,Rest cannot be enjoyed by lazy people.~前端
在django路由層的文章中介紹瞭如何進行路由和視圖函數的匹配關係,本文將介紹django的視圖函數,即django的視圖層。python
在路由層進行路由和視圖函數的匹配,路由匹配成功後將會執行對應的視圖函數,視圖函數就是用來處理對應url的請求而且返回響應數據,在這個請求-響應的過程當中大體涉及到兩點,第一就是視圖層會和模型層(數據相關)進行交互,對數據進行增刪改查,第二是視圖層須要將某些數據傳遞給前端HTML頁面。django
在django請求生命週期中,客戶端瀏覽器發送的請求經過網關以後會將客戶端的請求解析成HTTP格式的數據封裝到request對象中,在視圖函數中的每一個函數都必須有request形參(名字能夠任意,約定俗成是request)來接收request對象,在若是在視圖函數中打印request對象會獲得什麼呢?編程
def index(request):
print(request) # <WSGIRequest: GET '/first/index/'>,表示請求方式爲get,請求的路由是/first/index
return HttpResponse('js_str')
複製代碼
request對象中有不少方法能夠獲取客戶端瀏覽器請求的具體數據,好比請求方式、請求攜帶的數據等。結合前端的form表單一塊兒來看看request對象的方法,主要分爲request.GET
和request.POST
兩方面,分別表示客戶端瀏覽器的get請求和post請求。json
form表單的默認請求方式就是get,當請求方式是get時前端向後端提交的數據會直接跟在url的後面以?
做爲參數和url的分隔符,不一樣參數之間以&
鏈接,問號後面攜帶的參數對路由沒有任何影響,只是向後端傳送get請求方式的數據。後端
注意:get請求通常是用來請求頁面的,問號後邊攜帶的參數一般用來在後端進行數據篩選之類的操做,而不用於前端向後端提交數據。任何第一次打開的頁面發送的請求都是get請求。瀏覽器
<!--前端html頁面:mu.html-->
<body>
<form method="get">
用戶名:<input type="text" name="username" placeholder="請輸入用戶名" id="username">
密碼:<input type="text" name="pwd" placeholder="請輸入密碼" id="pwd">
<input type="submit" value="登陸">
</form>
</body>
複製代碼
# views.py
def login(request):
print(f'請求方式{request.method}')
print(request.get_full_path()) # 獲取完整的url及問號後面的參數
print(request.path) # 獲取url
print(f'get請求攜帶的數據{request.GET}') # 獲得的是相似字典的對象,取值方式和字典相同
print('獲取請求攜帶的數據')
print(request.GET.get('username'))
print(request.GET.get('pwd'))
return render(request, 'mu.html')
複製代碼
當訪問127.0.0.1:8000/login
時,會打開mu.html
頁面。輸入用戶名和密碼以後點擊登陸
按鈕,瀏覽器的地址欄中就會變成http://127.0.0.1:8000/login/?username=root&pwd=123
,然後端對應的視圖函數會輸出對應的結果,以下:微信
請求方式GET
/first/login/?username=root&pwd=123
/first/login/
get請求攜帶的數據<QueryDict: {'username': ['root'], 'pwd': ['123']}>
獲取請求攜帶的數據
root
123
複製代碼
通常post請求是用來向後端提交數據,當使用post請求方式向後端發送請求時,提交的數據將再也不顯示在url的後面。post請求的前端頁面使用HTML文章中的前端註冊頁面。markdown
# views.py
def register(request):
if request.method == 'POST':
print(f'請求方式{request.method}')
# request.POST,獲取用戶提交的數據,不包括文件,每一個鍵值對中的值都是列表
res = request.POST
# res.get('username'),只獲取列表中的最後一個元素
print(res.get('username'), type(res.get('username')))
# res.getlist('hobby')直接將列表取出
print(res.getlist('hobby'), type(res.getlist('hobby')))
# 獲取文件數據
print(request.FILES)
avatar_obj = request.FILES.get('avatar') # 獲取文件對象
print(avatar_obj.name) # 獲取文件名稱
# 保存文件至服務端的靜態文件中
with open(avatar_obj.name, 'wb') as f:
for line in avatar_obj.chunks(): # 保存文件數據須要使用chunks()
f.write(line)
return HttpResponse('註冊成功')
return render(request, 'render_html.html')
複製代碼
當訪問127.0.0.1:8000/register
時,首先會打開render_html.html
頁面,輸入用戶名和密碼以後點擊提交,前端輸入的數據將會傳給後端,後端視圖函數的執行結果以下:
請求方式POST
root <class 'str'> ['read', 'outdoors'] <class 'list'> <MultiValueDict: {'avatar': [<InMemoryUploadedFile: 5076e4181de45b78727ffca4e1f8e0a.png (image/png)>]}>
5076e4181de45b78727ffca4e1f8e0a.png
複製代碼
除了上述的request對象的方法,還有一些其餘經常使用的request對象方法:
# 原生瀏覽器發過來的二進制數據
print(request.body)
# 獲取url
print(request.path)
# 獲取完整的url及問號後面的參數
print(request.get_full_path())
複製代碼
在視圖函數中最基本的三種向前端頁面返回數據的方式:
from django.shortcuts import render, redirect, HttpResponse
# HttpResponse返回字符串給前端
return HttpResponse('hello world')
# render,將模板文件渲染成一個html文件而後返回給瀏覽器,還支持給模板文件傳數據
return render(request, 'index.html', locals())
# redirect, 重定向
return redirect('/index/')
複製代碼
這三者內部返回的都是HttpResponse對象,所以視圖函數必須返回一個HttpResponse對象。
老生常談,json格式的數據一般用來不一樣編程語言之間進行數據交互,先後端數據的交互也可使用json格式的數據進行傳輸。可使用json
模塊進行序列化:
import json
from django.shortcuts import HttpResponse
def my_json(request):
user_dict = {'name':'python','password':'123'}
js_str = json.dumps(user_dict,ensure_ascii=False)
return HttpResponse(js_str)
複製代碼
django框架提供了另種更加方便的方法將json格式的數據JsonResponse
,JsonResponse
也是一個類,該類繼承了HttpResponse
用法以下:
from django.http import JsonResponse
def ab_json(request):
user_dic = {'name':'小莊','password':'123'}
# JsonResponse方式 --- 經過源碼掌握用法,若是json_dumps_params參數中ensure_ascii的值爲真,返回的序列化字符串中文將不能正常顯示
return JsonResponse(user_dic, json_dumps_params={'ensure_ascii':False})
複製代碼
須要注意的是,JsonResponse
默認只能序列化字典,若是想要序列化其餘格式的數據須要指定safe參數爲False,不然會拋出異常。源碼以下:
# JsonResponse部分源碼
class JsonResponse(HttpResponse):
def __init__(self, data, encoder=DjangoJSONEncoder, safe=True, json_dumps_params=None, **kwargs):
if safe and not isinstance(data, dict):
raise TypeError(
'In order to allow non-dict objects to be serialized set the '
'safe parameter to False.'
)
if json_dumps_params is None:
json_dumps_params = {}
kwargs.setdefault('content_type', 'application/json')
data = json.dumps(data, cls=encoder, **json_dumps_params)
super().__init__(content=data, **kwargs)
複製代碼
所以在對非字典類型的數據類型進行序列化時須要指定safe參數:
from django.http import JsonResponse
def test(request):
l = ['a', '莊']
return JsonResponse(l, safe=False, json_dumps_params={'ensure_ascii': False})
複製代碼
視圖層中的進行邏輯處理部分的代碼既能夠是函數,也能夠是類,在這以前的視圖層的邏輯處理都是函數處理請求,即FBV(function base views),下面就來介紹CBV是如何與url進行對應並處理邏輯的。
CBV(class base views),在視圖中使用類處理請求,python是一個面向對象的編程語言,若是隻用函數進行開發,可能不少面向對象的優勢就錯失了(封裝、繼承、多態),所以Django在後來加入了Class-Based-View。可讓咱們用類寫View。這樣作的優勢主要下面兩種:
①提升了代碼的複用性,可使用面嚮對象的技術,好比Mixin(多繼承)
②能夠用不一樣的函數針對不一樣的HTTP方法處理,而不是經過不少if判斷,提升代碼可讀性
CBV的特色就是可以根據請求方式的不一樣直接匹配到對應的方法執行,下面是使用CBV的視圖層和路由層的代碼:
# views.py
from django.views import View
class CBV(View):
def get(self, request):
return HttpResponse('get')
def post(self, request):
return HttpResponse('post')
# urls.py
from django.conf.urls import url
from first import views
urlpatterns = [
url(r'cbv/', views.CBV.as_view())
]
複製代碼
當訪問127.0.0.1:8000/cbv
時,會根據請求方式的不一樣自動執行CBV類中對應的方法,那麼django是如何實現這一效果的呢?就須要從源碼入手分析,請求到達django後端時首先須要通過路由層,即urls.py,路由匹配以後纔會執行對應的視圖函數,所以就從urls.py
着手進行源碼分析,這裏就要注意對象的屬性查找順序,千萬不能搞錯了噢~先從類自己查找,找不到去父類中找,必定要按照這個順序。
首先查看as_view
的源碼,發現是類方法,該方法調用後會返回閉包函數view
,因而此時url與視圖函數的關係變成了url(r'cbv/', views.view)
,這就和FBV的形式相同了。
再看閉包函數view
,在該函數內實例化視圖層定義的CBV
類,並調用對象的dispatch
方法,因爲自定義的CBV
類中並無該方法,按照對象的屬性查找順序,須要去父類View
中查找dispatch
方法,換句話說若是在CBV
中定義了dispatch
方法就會覆蓋View
類中的。
最後在dispatch
方法內採用了反射的方式,根據請求方式的不一樣動態匹配到視圖類中對應的方法中。
文章首發於微信公衆號**「程序媛小莊」**,同步於掘金。
碼字不易,轉載請說明出處,走過路過的小夥伴們伸出可愛的小指頭點個贊再走吧(╹▽╹)