zoukankan      html  css  js  c++  java
  • 🍖Django框架之视图层(CBV源码剖析) ⭐

    引入

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

    一.三板斧本质

    • HttpResponse、render、redirect

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

    image-20210317201913593

    提示你没有返回一个 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 源码

    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 对象

    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方法--->"
    
  • 相关阅读:
    Pikachu-RCE【远程代码、命令执行漏洞】
    Pikachu-SQL注入
    Pikachu-CSRF【跨站请求伪造】
    DWVA-xss【跨脚本漏洞】三类
    git 本地修改未提交时切换分支
    windows密码修改后同步修改git认证密码,HTTP Basic: Access denied and fatal Authentication
    git远程仓库回退
    git版本回退
    idea强制更新Maven依赖包
    IDEA多Module项目
  • 原文地址:https://www.cnblogs.com/songhaixing/p/14583239.html
Copyright © 2011-2022 走看看