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方法--->"
    
  • 相关阅读:
    CodeForces 288A Polo the Penguin and Strings (水题)
    CodeForces 289B Polo the Penguin and Matrix (数学,中位数)
    CodeForces 289A Polo the Penguin and Segments (水题)
    CodeForces 540C Ice Cave (BFS)
    网站后台模板
    雅图CAD
    mbps
    WCF学习-协议绑定
    数据库建表经验总结
    资源位置
  • 原文地址:https://www.cnblogs.com/songhaixing/p/14583239.html
Copyright © 2011-2022 走看看