🍖Django框架之視圖層(CBV源碼剖析) ⭐

引入

視圖層是實現業務邏輯的關鍵層html

一.三板斧本質

  • HttpResponse、render、redirect前端

  • 視圖函數必需要返回一個 HttpResponse 對象, 若是未返回, 會出現以下錯誤 :python

image-20210317201913593

提示你沒有返回一個 HttpResponse 對象, 而是返回了一個 Nonedjango

  • 爲何必須返回這個對象呢? 咱們 Ctrl + 鼠標點擊分別查看三者的源碼可獲得解釋
# HttpResponse

# render
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)  # 返回一個HttpResponse對象
        
# redirect
內部繼承了 HttpResponse 類

二.JsonResponse 對象

1.Json格式的做用

  • JSON的全稱是"JavaScript Object Notation", 意思是JavaScript對象表示法
  • 先後端的交互通常使用 JSON 實現數據的跨域傳輸

2.實現給前端返回 json 格式數據

  • 方式一 : 直接本身序列化

import json

def test(resquest):
    user_info = {"name":"shawn","age":23,"sex":"male","hobby":"吃飯"}
    data = json.dumps(user_info,ensure_ascii=False)
    return HttpResponse(data)

添加 ensure_ascii=False 參數是爲了讓中文保持正常顯示, 否則會轉換成uncode格式json

  • 方式二 : 使用JsonResponse對象

from django.http import JsonResponse

def test2(request):
    user_info = {"name":"shawn","age":23,"sex":"male","hobby":"吃飯"}
    return JsonResponse(user_info)

疑問 : JsonResponse 對象沒有 ensure_ascii 參數來保證中文正常顯示嗎?後端

解決方法 : 查看 JsonResponse 源碼跨域

image-20210317214153965

那咱們就爲其傳入 json_dumps_params( ) 參數閉包

return JsonResponse(user_info, json_dumps_params={'ensure_ascii':False})
# 測試能夠正常顯示中文
  • 加入 safe=False 參數, 讓其容許非 dict 對象被序列化

image-20210317212941446

from django.http import JsonResponse
def test1(request):
    ll = [1,2,3,43,4]
    return JsonResponse(ll, safe=False,json_dumps_params={'ensure_ascii':False})

image-20210317214609711

ps : JsonResponse 返回的也是 HttpResponse 對象app

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

三.form 表單上傳文件

1.form表單上傳文件注意事項

  • method 必須指定 post 提交
  • enctype 指定屬性值 : multipart/form-data

2.代碼實現

  • test.html 文件
<body>
<div class="container">
    <div class="col-md-8 col-md-offset-2">
        <form action="" method="post" enctype="multipart/form-data">
            <input type="file" class="form-control" name="myfile">
            <input type="submit" class="btn btn-warning btn-block" value="提交">
        </form>
    </div>
</div>
</body>

image-20210317222610019

  • views.py 文件
def form_test(request):
    if request.method == "POST":
        # 從文件對象字典中獲取名爲"myfile"的文件對象
        file_obj = request.FILES.get('myfile')  
        file_name = file_obj.name
        # 保存方式一:
        with open(f'./{file_name}',"wb")as f:
            for line in file_obj:
                f.write(line)
        # 保存方式二:(官方推薦)
        with open(f'./{file_name}',"wb")as f:
            for line in file_obj.chunks():
                f.write(line)

    return render(request,'test.html')

# 打印個別結果看看
print(request.FILES)
'''
<MultiValueDict: {'myfile': [<InMemoryUploadedFile: README.md (application/octet-stream)>]}>
'''
print(file_obj,type(file_obj))
'''
README.md <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
'''
print(file_obj.name)
'''
README.md
'''

# 官方推薦寫法 chunks() 的源碼
    def chunks(self, chunk_size=None):
        self.file.seek(0)
        yield self.read()
# 一個迭代器

四.FBV與CBV

1.FBV : ( function-based-view ) 基於函數的視圖

  • 咱們以前在視圖層寫的都是基於函數的視圖

2.CBV : ( class-based-view ) 基於類的視圖

  • 特色 : 可以根據請求方式的不一樣, 直接觸發對應方法的執行
  • 基本使用
# views.py 文件
from django.views import View

def test_func(request):
    return render(request,'test.html')
    
class MyView(View):
    def get(self,request):
        return HttpResponse('觸發了get方法--->')
    
    def post(self,request):
        return HttpResponse('觸發了post方法--->')

# urls.py 文件
from django.urls import path,re_path,include
from app02 import views

urlpatterns = [
    re_path(r'^test/',views.test_func),
    re_path(r'^func',views.MyView.as_view())
]

# 模板文件 test.html
<div>
    <form action="/func/" method="post" enctype="multipart/form-data">
        <input type="file">
        <input type="submit">
    </form>
</div>
  • 實現效果 : 用戶使用get方式提交數據觸發get方法, 用戶使用post方式提交數據觸發post方法

image-20210318162922305

image-20210318162957803

image-20210318163008337

那麼爲何就能根據不一樣的操做來觸發不一樣發那個發的執行呢?函數

五.CBV源碼分析

1.涉及知識

  • 面向對象屬性查找順序
  • 類方法特性
  • 反射中getattr( ) 提早了解一下

2.突破口

re_path(r'^func',views.MyView.as_view())  # as_view() 是什麼東西

咱們 Ctrl + 點擊查看其源碼

發現它是一個類方法, 查看其總體結構(只看框起來的便可, 其餘的不用管), 該方法內部有一個 view 方法, 而且返回值是 view 的內存地址, 相似於閉包函數

image-20210318165421598

因而咱們就能夠獲得一些初步結果

re_path(r'^func',views.MyView.as_view())  # 等同於下面
re_path(r'^func',views.view) # 看着是否是與普通的路由沒有什麼區別了 : 經過匹配觸發視圖函數的運行

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

咱們再看其源碼(只看框起來的便可,其餘的不用管) :

image-20210318170758491

"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類方法的下面找到了

image-20210318171904242

咱們在看它下面的邏輯代碼

image-20210318172023112

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

# 先是拿到當前請求方式的大寫字符轉成小寫, 而後判斷在不在後面的 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+點擊 也來看看:

image-20210318173044730

# 原來是一個報錯信息 : 提示方法不容許,整理下思路,也就是說self中有get返回值或者內存地址,沒有則報錯
# 很顯然咱們的self是有get這個名字的,而且是一個方法,因而將get方法的內存地址賦值給handler
# 咱們再來看dispatch的返回值 : handler + (括號), 不就是執行該方法嗎!也就是執行了咱們的get方法打印了"觸發了get方法--->"
相關文章
相關標籤/搜索