Django之檢視層

2022-12-15 06:00:29

檢視函數

檢視層介紹

一個檢視函數,簡稱檢視,是一個簡單的 Python 函數,它接受 Web 請求並且返回 Web 響應。

響應可以是一個 HTML 頁面、一個 404 錯誤頁面、重定向頁面、XML 檔案、或者一張圖片…

每個檢視函數都負責返回一個 HttpResponse 物件,物件中包含生成的響應。

程式碼寫在哪裡都可以,只要在 Python 目錄下面,一般約定是將檢視放置在專案或應用程式目錄中的名為views.py的檔案中。

檢視層作用

  • 作用:主要存放業務核心邏輯程式碼

檢視層,熟練掌握兩個物件即可:請求物件(request)響應物件(HttpResponse)

檢視層之必會三板斧

django檢視函數必須要返回一個HttpResponse物件
否則報錯:The view app01.views.func1 didn't return an HttpResponse object. It returned None instead.提示你沒有返回一個Httpresponse物件而是返回了一個None。

 

 為什麼必須要返回這個物件呢?我們 Ctrl + 滑鼠點選分別檢視三者的原始碼來查探究竟。

HttpResponse原始碼

class HttpResponse(HttpResponseBase):
    """
    An HTTP response class with a string as content.

    This content that can be read, appended to or replaced.
    """
    streaming = False

    def __init__(self, content=b'', *args, **kwargs):
        super(HttpResponse, self).__init__(*args, **kwargs)
        # Content is a bytestring. See the `content` property methods.
        self.content = content 

由此可得,HttpResponse()就是物件,括號內直接跟一個具體的字串作為響應體,範例如下

HttpResponse(): 返回文字,引數為字串,字串中寫文字內容。如果引數為字串裡含有 html 標籤,也可以渲染。
 def runoob(request):
    # return HttpResponse("百度首頁")
    return HttpResponse("<a href='https://www.baidu.com/'>百度首頁</a>")

render原始碼

  • 底層返回的也是 HttpResponse 物件
   
def render(request, template_name, context=None, content_type=None, status=None, using=None):
        """
        Returns a HttpResponse whose content is filled with the result of calling
        django.template.loader.render_to_string() with the passed arguments.
        """
        content = loader.render_to_string(template_name, context, request, using=using)
        return HttpResponse(content, content_type, status)
  
'''
render(request, template_name[, context])`  `結合一個給定的模板和一個給定的上下文字典,並返回一個渲染後的 HttpResponse 物件。
引數:
     request: 用於生成響應的請求物件。

     template_name:要使用的模板的完整名稱,可選的引數

     context:新增到模板上下文的一個字典。預設是一個空字典。如果字典中的某個值是可呼叫的,檢視將在渲染模板之前呼叫它。

render方法就是將一個模板頁面中的模板語法進行渲染,最終渲染成一個html頁面作為響應體。
'''

redirect原始碼

  • redirect內部是繼承了HttpRespone類

 

 

 

 

小結:

響應物件

  • 響應物件主要有三種形式:HttpResponse(),render(),redirect()
  • HttpResponse():返回文字,引數為字串,字串中寫文字內容。
  • render():返回文字,第一個引數為request,第二個引數為 字串(或html頁面檔案),第三個引數為字典(可選引數,向頁面傳遞的引數:鍵為頁面引數名,值為views引數名)
  • redirect():重定向,跳轉到新頁面。引數為字串,字串中填寫頁面路徑。一般用於form提交資料後跳轉到新頁面

三板斧本質

  • ender 和 redirect 是在 HttpResponse 的基礎上進行了封裝:
  • render:底層返回的也是 HttpResponse 物件
  • redirect:底層繼承的是 HttpResponse 物件

JsonResponse物件

1.作用

全稱:JSON的全稱是"JavaScript Object Notation", 意思是JavaScript物件表示法

作用:前後端互動一般使用的是json實現資料的跨域傳輸

2.向前端返回一個json格式字串的兩種方式

方法一:直接自己序列化

import json

def index_func(request):
    user_dict = {'name':'alex','age':'18','性別':''}
    user_json = json.dumps(user_dict,ensure_ascii=False)
    return HttpResponse(user_json)

# 新增ensure_ascii=False 引數是為了讓中文保持正常顯示, 不然會轉換成uncode格式

 方法二:使用JsonResponse物件

from django.http import JsonResponse

def index_func(request):
    user_dict = {'name':'alex','age':'18','性別':''}
    return JsonResponse(user_dict)

 

 

問題:bJsonResponse 物件沒有 ensure_ascii 引數來保證中文正常顯示嗎?

首先,我們來檢視原始碼。

 

 由原始碼可知,json_dumps_params是一個字典,接下來我們為json_dumps_params傳入引數。

from django.http import JsonResponse

def index_func(request):
    user_dict = {'name':'alex','age':'18','性別':''}
    return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False})

ps:以後寫程式碼很多時候可能需要參考原始碼及所學知識擴充套件功能

class JsonResponse():
    def __init__(self,data,json_dumps_params=None):
        json.dumps(data,**json_dumps_params)

JsonResponse主要序列化字典,針對非字典的其他可以被序列化的資料需要修改safe引數為False

from django.http import JsonResponse

def index_func(request):
    user_list = [11,22,33,44,55]
    return JsonResponse(user_list,json_dumps_params={'ensure_ascii':False})

 

提示為了讓非字典物件能夠被序列化,設定safe引數為false。

我程式碼沒有寫這個啊,這是哪來的呢?

憑空捏造??且看JsonResonse原始碼

 加入 safe=False 引數, 讓其允許非 dict 物件被序列化

from django.http import JsonResponse

def index_func(request):
    user_list = [11,22,33,44,55]
    return JsonResponse(user_list,safe=False,json_dumps_params={'ensure_ascii':False})

ps:JsonResponse返回的也是HttpResponse物件

class JsonResponse(HttpResponse):  # 繼承了HttpResponse
    ...

檢視層之request物件獲取檔案

request.FILES    # 獲取檔案物件

1.form表單上傳檔案注意事項

  1. method必須是post
  2. enctype引數修改為multipart/form-data

2.實現程式碼

index_Page.html檔案

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>
</head>
<body>
    <form action="" method="post" enctype="multipart/form-data">
        <p>file:
            <input type="file" name="file" multiple="multiple">
        </p>
        <input type="submit" value="提交">
    </form>
</body>
</html>

views.py檔案

from django.shortcuts import render

def index_func(request):
    if request.method == 'POST':
        # 獲取檔案資料 
        print(request.FILES)    # <MultiValueDict: {'file': [<InMemoryUploadedFile: 試卷.pdf (application/pdf)>]}>
     # 獲取檔案資料物件   
        file_obj = request.FILES.get('file')
        print(file_obj,type(file_obj))      # 試卷.pdf   <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
        # 獲取檔案物件字串名
        print(file_obj.name,type(file_obj.name))    # 試卷.pdf   <class 'str'>
        
        with open(r'%s' % file_obj.name, 'wb') as f:
            for line in file_obj:
                f.write(line)

    return render(request,'indexPage.html')         

FBV與CBV

FBV基於函數的檢視(Function base view)

  • 之前我們在檢視層都用的是基於函數的檢視,使用函數來處理不同的請求。

基於檢視的函數我們學習Django的時候就就已經在使用了,範例如下:

urls.py檔案

urlpatterns = [
    path("login/", views.login),
]

views.py檔案

from django.shortcuts import render,HttpResponse

def login(request):
    if request.method == "GET":
        return HttpResponse("GET 方法")
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")
        if user == "shawn" and pwd == "123456":
            return HttpResponse("POST 方法")
        else:
            return HttpResponse("POST 方法1")

如果我們在瀏覽器中直接存取 http://127.0.0.1:8000/login/ ,輸出結果為:GET 方法

CBV基於類的檢視(Class base view)

基本介紹

  • 採用採用物件導向的方法寫檢視檔案。
  • 使用類來處理檢視層的請求

基本使用

檢視層views.py檔案

from django import views

class MyloginView(views.View):
    def get(self, request):
        return HttpResponse('from CBV get function')
    def post(self,request):
        return HttpResponse('from CBV post function')

路由層urls.py檔案

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('login/',views.MyloginView.as_view())
]

CBV會自動根據請求方法的不同自動匹配對應的方法並執行

CBV原始碼剖析(重要)

1.儲備知識

  • 物件導向屬性查詢順序
  • 類方法特性
  • 反射中getattr( ) 提前瞭解一下

2.突破口

path('login/', views.MyLoginView.as_view())    # as_view() 是什麼東西

我們 Ctrl + 點選檢視其原始碼

發現它是一個類方法, 檢視其整體結構(只看框起來的即可, 其他的不用管), 該方法內部有一個 view 方法, 並且返回值是 view 的記憶體地址, 類似於閉包函數

 

 於是我們就可以得到一些初步結果

path('login/', views.MyLoginView.as_view())    # 等同於下面
path('login',views.view)    # 看著是不是與普通的路由沒有什麼區別了 : 通過匹配觸發檢視函數的執行

那麼 view 是一個什麼樣的函數呢? 現在突破口變成了 view 方法了

我們再看其原始碼(只看框起來的即可,其他的不用管) :

"self = cls(**initkwargs)"
# cls是什麼? 記得上面的類方法嗎? 類呼叫時傳入類本身
# 我們是通過MyView來呼叫as_view的, 那麼cls也就是MyView
# 類加括號範例化得到物件self, 這個self就是我們自己的類產生的物件 : self=MyView(**initkwargs),我們不用去管裡面的引數
# 接下來看看view的返回值 : self.dispatch(request, *args, **kwargs)
# 也就是去MyView類範例出的物件裡面去找dispatch方法並執行,很顯然self物件中沒有該方法,於是去類中去找,也沒有
# 最後到父類別View中去找,發現就在as_view類方法的下面找到了

 

 我們在看它下面的邏輯程式碼

 

 邏輯很簡單,使用了反射的知識點

# 先是拿到當前請求方式的大寫字元轉成小寫, 然後判斷在不在後面的 self.http_method_names 裡面
# Ctrl+點選 看看這是個什麼東西 : 
'http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']'
# 發現是8種常用的請求方式列表, 接著返回dispatch原始碼檢視,為了方便我們假設現在的是get請求方式
# 判斷get請求在請求列表裡面,於是執行緊跟其下的程式碼...我們先看看getattr()得到的是什麼結果
# 判斷我們的self是否有名叫get的屬性或方法,如果有則返回該屬性的值或方法的記憶體地址,否則返回 self.http_method_not_allowed, 這是個啥,我們 Ctrl+點選 也來看看:

# 原來是一個報錯資訊 : 提示方法不允許,整理下思路,也就是說self中有get返回值或者記憶體地址,沒有則報錯
# 很顯然我們的self是有get這個名字的,並且是一個方法,於是將get方法的記憶體地址賦值給handler
# 我們再來看dispatch的返回值 : handler + (括號), 不就是執行該方法嗎!也就是執行了我們的get方法列印了"觸發了get方法--->"