Django視圖層 - 視圖層如何處理客戶端請求?| Python 主題月

本文正在參加「Python主題月」,詳情查看 活動連接html

微信公衆號搜索【程序媛小莊】,Rest cannot be enjoyed by lazy people.~前端

前言

django路由層的文章中介紹瞭如何進行路由和視圖函數的匹配關係,本文將介紹django的視圖函數,即django的視圖層。python

視圖層的功能

在路由層進行路由和視圖函數的匹配,路由匹配成功後將會執行對應的視圖函數,視圖函數就是用來處理對應url的請求而且返回響應數據,在這個請求-響應的過程當中大體涉及到兩點,第一就是視圖層會和模型層(數據相關)進行交互,對數據進行增刪改查,第二是視圖層須要將某些數據傳遞給前端HTML頁面。django

request對象

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.GETrequest.POST兩方面,分別表示客戶端瀏覽器的get請求和post請求。json

request.GET

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
複製代碼

request.POST

通常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對象的方法,還有一些其餘經常使用的request對象方法:

# 原生瀏覽器發過來的二進制數據
print(request.body)

# 獲取url
print(request.path)

# 獲取完整的url及問號後面的參數
print(request.get_full_path())
複製代碼

HttpResponse & render & redirect

在視圖函數中最基本的三種向前端頁面返回數據的方式:

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對象。

JsonResponse

老生常談,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格式的數據JsonResponseJsonResponse也是一個類,該類繼承了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和CBV

視圖層中的進行邏輯處理部分的代碼既能夠是函數,也能夠是類,在這以前的視圖層的邏輯處理都是函數處理請求,即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方法內採用了反射的方式,根據請求方式的不一樣動態匹配到視圖類中對應的方法中。

image-20210714112402429

結語

文章首發於微信公衆號**「程序媛小莊」**,同步於掘金

碼字不易,轉載請說明出處,走過路過的小夥伴們伸出可愛的小指頭點個贊再走吧(╹▽╹)

image.png

相關文章
相關標籤/搜索