10 Django框架之视图层(CBV源码剖析) ⭐

92次阅读
没有评论

共计 4500 个字符,预计需要花费 12 分钟才能阅读完成。

引入

视图层是实现业务逻辑的关键层

一. 三板斧本质

  • HttpResponse、render、redirect

  • 视图函数必须要返回一个 HttpResponse 对象, 如果未返回, 会出现如下错误 :

10 Django 框架之视图层 (CBV 源码剖析) ⭐

提示你没有返回一个 HttpResponse 对象, 而是返回了一个 None

  • 为什么必须返回这个对象呢? 我们 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 格式

  • 方式二 : 使用 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 源码

10 Django 框架之视图层 (CBV 源码剖析) ⭐

那我们就为其传入 json_dumps_params() 参数

return JsonResponse(user_info, json_dumps_params={'ensure_ascii':False})
# 测试可以正常显示中文
  • 加入 safe=False 参数, 让其允许非 dict 对象被序列化

10 Django 框架之视图层 (CBV 源码剖析) ⭐

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})

10 Django 框架之视图层 (CBV 源码剖析) ⭐

ps : JsonResponse 返回的也是 HttpResponse 对象

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>

10 Django 框架之视图层 (CBV 源码剖析) ⭐

  • 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 方法

10 Django 框架之视图层 (CBV 源码剖析) ⭐

10 Django 框架之视图层 (CBV 源码剖析) ⭐

10 Django 框架之视图层 (CBV 源码剖析) ⭐

那么为什么就能根据不同的操作来触发不同发那个发的执行呢?

五.CBV 源码分析

1. 涉及知识

  • 面向对象属性查找顺序
  • 类方法特性
  • 反射中 getattr() 提前了解一下

2. 突破口

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

我们 Ctrl + 点击查看其源码

发现它是一个类方法, 查看其整体结构 (只看框起来的即可, 其他的不用管), 该方法内部有一个 view 方法, 并且返回值是 view 的内存地址, 类似于闭包函数

10 Django 框架之视图层 (CBV 源码剖析) ⭐

于是我们就可以得到一些初步结果

re_path(r'^func',views.MyView.as_view())  # 等同于下面
re_path(r'^func',views.view) # 看着是不是与普通的路由没有什么区别了 : 通过匹配触发视图函数的运行

那么 view 是一个什么样的函数呢? 现在突破口变成了 view 方法了

我们再看其源码 (只看框起来的即可, 其他的不用管) :

10 Django 框架之视图层 (CBV 源码剖析) ⭐

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

10 Django 框架之视图层 (CBV 源码剖析) ⭐

我们在看它下面的逻辑代码

10 Django 框架之视图层 (CBV 源码剖析) ⭐

逻辑很简单, 使用了反射的知识点

# 先是拿到当前请求方式的大写字符转成小写, 然后判断在不在后面的 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+ 点击 也来看看:

10 Django 框架之视图层 (CBV 源码剖析) ⭐

# 原来是一个报错信息 : 提示方法不允许, 整理下思路, 也就是说 self 中有 get 返回值或者内存地址, 没有则报错
# 很显然我们的 self 是有 get 这个名字的, 并且是一个方法, 于是将 get 方法的内存地址赋值给 handler
# 我们再来看 dispatch 的返回值 : handler + (括号), 不就是执行该方法吗! 也就是执行了我们的 get 方法打印了 " 触发了 get 方法 --->"
正文完
 
shawn
版权声明:本站原创文章,由 shawn 2023-06-16发表,共计4500字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)