【7.0】Django框架之视图层
【一】三板斧
【1】HttpResponse
- 返回字符串类型
【2】render
- 返回html页面,并且在返回给浏览器之前还可以给html页面传值
【3】redirect
- 重定向页面
视图函数必须返回一个 HttpResponse 对象
【二】JsonResponse
- 路由配置
# json相关
url('^ab_json/',views.ab_json),
【1】json模块序列化
def ab_json(request):
    user_dict = {
        "username": "dream你好",
        'age': 18,
        'password': 123123,
    }
    # 先转成JSON格式字符串
    # ensure_ascii:中文不编码
    json_str = json.dumps(user_dict, ensure_ascii=False)
    # 然后将该字符串返回
    return HttpResponse(json_str)
【2】JsonResponse对象
(1)问题引入
def ab_json(request):
    user_dict = {
        "username": "dream你好",
        'age': 18,
        'password': 123123,
    }
   
    return JsonResponse(user_dict)
- 返回的页面展示的数据中,中文字符被强制编码了,如何解决?
class JsonResponse(HttpResponse):
    def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
                 json_dumps_params=None, **kwargs):
        if safe and not isinstance(data, dict):
            raise TypeError(
                'In order to allow non-dict objects to be serialized set the '
                'safe parameter to False.'
            )
        if json_dumps_params is None:
            json_dumps_params = {}
        kwargs.setdefault('content_type', 'application/json')
        # 数据序列化发生在这里
        data = json.dumps(data, cls=encoder, **json_dumps_params)
        super(JsonResponse, self).__init__(content=data, **kwargs)
- 通过分析源码我们可以发现数据表被序列化的位置
- 其中 有一个参数  **json_dumps_params- 这个参数意思是将数据打散传入到前面的方法中
 
(2)解决方案
def ab_json(request):
    user_dict = {
        "username": "dream你好",
        'age': 18,
        'password': 123123,
    }
    return JsonResponse(user_dict, json_dumps_params={'ensure_ascii': False})
- 将我们需要的参数以字典的形式传进去
(3)其他数据类型序列化
- 我们不仅有字典格式的数据可以被序列化也有其他操作
- 例如列表
def ab_json(request):
    user_dict = {
        "username": "dream你好",
        'age': 18,
        'password': 123123,
    }
    user_list = [11, 22, 33, 44, 55, 66, 77, 88, 99]
    return JsonResponse(user_list, json_dumps_params={'ensure_ascii': False})
- 我们在访问数据的时候会提示下面的错误
In order to allow non-dict objects to be serialized set the safe parameter to False.
- 我们按照提示加上需要的参数
def ab_json(request):
    user_dict = {
        "username": "dream你好",
        'age': 18,
        'password': 123123,
    }
    user_list = [11, 22, 33, 44, 55, 66, 77, 88, 99]
    return JsonResponse(user_list, safe=False, json_dumps_params={'ensure_ascii': False})
- JsonResponse在序列化字典以外的数据格式时,会有一个安全设置,我们将参数修改即可修改数据
【二】form表单文件上传下载
form表单上传数据以及后端如何获取
form知识回顾
<form action="" method="post" enctype="multipart/form-data"></form>
- form表单想要上传文件类型的数据
- method 参数必须改为post
- enctype 参数必须指定成 form-data 类型
 
【1】数据准备
- 路由
# form 表单上传 下载文件
url('^ab_file/',views.ab_file),
- 前端
<form action="" method="post" enctype="multipart/form-data" class="form form-control">
    <p>username:<input type="text" name="username" class="form-control"></p>
    <p>file:<input type="file" name="file" class="form-control"></p>
    <input type="submit">
</form>
【2】数据处理
(1)POST请求数据
def ab_file(request):
    if request.method == 'POST':
        # 只能获取到普通的文本数据,无法获取到文件数据
        print(request.POST)
    return render(request, 'file.html')
<QueryDict: {'username': ['dream']}>
- 通过这种方式,我们只能获取到我们输入的文本数据,而拿不到我们想要的文件数据
(2)文件数据获取
def ab_file(request):
    if request.method == 'POST':
        # 获取文件数据
        print(request.FILES) # <MultiValueDict: {'file': [<InMemoryUploadedFile: img.png (image/png)>]}>
        # 提取文件数据 - 文件对象
        file_obj = request.FILES.get('file')
        # 提取文件名字 file_obj.name
        with open(file_obj.name, 'wb') as f:
            # 逐行读取文件数据
            # 官方推荐 加上 chunks 方法 等价于 一行行获取
            for line in file_obj.chunks():
                f.write(line)
    return render(request, 'file.html')
<MultiValueDict: {'file': [<InMemoryUploadedFile: img.png (image/png)>]}>
【三】总结request对象方法
【1】request.method
- request.method:该方法返回客户端用于发起请求的HTTP方法。例如,可以是'GET'、'POST'、'PUT'、'DELETE'等。您可以使用该方法来确定请求的类型,并相应地执行特定操作。
【2】request.POST
- request.POST:该属性是一个类似字典的对象,包含了请求中通过POST方法发送的所有参数。这些参数通常是通过HTML表单发送的。您可以使用参数的名字作为键来访问单个参数,例如- request.POST['username']。
【3】request.GET
- request.GET:类似于- request.POST,该属性包含了请求中通过GET方法发送的所有参数。这些参数通常会附加在URL之后,以问号分隔。您可以使用参数的名字作为键来访问单个参数,例如- request.GET['page']。
【4】request.FILES
- request.FILES:该属性是一个类似字典的对象,包含了请求中通过文件上传组件发送的所有文件。当表单中包含文件上传字段时,通过- request.FILES可以访问上传的文件。您可以使用文件的名字作为键来访问单个文件,例如- request.FILES['file']。
【5】request.path
只能获取到路由地址,无法获取到参数
- request.path:该属性表示请求URL中的路径部分。它包含在域名之后,在任何查询参数之前。例如,如果请求的URL是"http://example.com/foo/bar/",那么- request.path将为"/foo/bar/"。
【6】request.path_info
只能获取到路由地址,无法获取到参数
- 用于表示请求URL的路径部分,不包括域名和查询参数。
- 与 request.path相比,request.path_info更加原始和未经解析。它保留了URL路径中的任何编码、特殊字符或斜杠等信息。
- 例如,对于以下请求URL:"http://example.com/foo/bar/?page=2",request.path_info的值将是 "/foo/bar/"。
- 通常情况下,您可以使用 request.path来获取丢弃域名后的路径,而使用request.path_info来获取原始的、未解析的路径。这在某些情况下非常有用,例如当您需要对URL进行一些自定义操作或路由处理时。
【7】request.get_full_path()
即能获取到路由地址又能获取到完整的路由地址后面的参数
- request.get_full_path():该方法返回请求URL的完整路径,包括路径部分和任何查询参数。当您需要将完整URL作为字符串使用时,这个方法非常有用。例如,如果请求的URL是"http://example.com/foo/bar/?page=2",- request.get_full_path()将返回"/foo/bar/?page=2"。
【四】FBV与CBV引入
视图函数既可以是函数也可以是类
【1】FBV
def index(request):
    return HttpResponse('index')
【2】CBV
(1)路由
# CBV 路由  -  根据请求方式的不同选择不同的入口动作
url('^login/', views.MyLogin.as_view())
(2)视图
from django.views import View
class MyLogin(View):
    def get(self, request, *args, **kwargs):
        return HttpResponse("get")
    def post(self, request, *args, **kwargs):
        return HttpResponse("post")
MBV和CBV各有各的特点,都有应用
- CBV特点
- 能够直接根据请求方式的不同直接匹配到对应的方法执行
【3】详解
FBV(Function-Based Views)与 CBV(Class-Based Views)是 Django 框架中用于处理视图(Views)的两种不同的编程方式。
(1)Function-Based Views(FBV):
- FBV 是 Django 最早支持的一种视图编程方式。
- 使用函数作为视图的处理逻辑。函数参数通常是 request 对象,表示用户发送的 HTTP 请求。
- 函数内部可以根据请求方法、参数、数据等进行相应的处理,并返回 HttpResponse 对象作为响应。
- 示例代码:from django.http import HttpResponse def my_view(request): # 处理逻辑 if request.method == 'GET': # 处理 GET 请求 return HttpResponse('Hello, GET Request!') elif request.method == 'POST': # 处理 POST 请求 return HttpResponse('Hello, POST Request!')
(2)Class-Based Views(CBV):
- CBV 是 Django 提供的另一种视图编程方式,引入了面向对象的概念。
- 使用类来表示视图,并使用类中的方法来处理不同类型的HTTP请求。
- Django 提供了许多内置的 CBV 类,可以方便地扩展和重写这些类来实现自定义功能。
- 示例代码:from django.http import HttpResponse from django.views import View class MyView(View): def get(self, request): # 处理 GET 请求 return HttpResponse('Hello, GET Request!') def post(self, request): # 处理 POST 请求 return HttpResponse('Hello, POST Request!')
(3)小结
- 选择使用 FBV 还是 CBV 取决于具体的需求和个人偏好。
- FBV 相对简单直观,适合编写简单的视图逻辑;
- 而 CBV 可以通过继承和重写类来实现代码复用和可扩展性,适用于复杂的视图处理场景。
- 在实际开发中,可以根据需求选择适合的方式来编写视图处理函数或类。
【五】CBV源码剖析
【1】as_view 方法
(1)路由 对应 函数内存地址
url(r'^login/', views.MyLogin.as_view()),
方法/函数名 加 括号 执行优先级最高
- 
可能的原因 - 要么是被@staicmethod方法修饰的静态方法
- 要么是被@classmethod方法修饰的类方法
 
- 
查看源码 - 绑定给类的静态方法,将类作为第一个参数传进去
 
	@classonlymethod
    def as_view(cls, **initkwargs):
        """
        Main entry point for a request-response process.
        """
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs
        # take name and docstring from class
        update_wrapper(view, cls, updated=())
        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view
- 
view 这是一个闭包函数 - 返回值是这个闭包函数的内存地址
 
- 
在启动Django项目时,就会立刻执行as_view方法 - 即
 
url(r'^login/', views.view()),
- 方法类似FBV方法
总结:CBV和FBV在路由匹配上本质上是一样的,都是路由 对应 函数内存地址
(2)view 方法剖析
    def view(request, *args, **kwargs):
        # 参数 cls 就是我们自己写的类
        self = cls(**initkwargs)
        # 等价于 self = MyLogin(**initkwargs) ---- 产生一个我们自己写的类的对象
        if hasattr(self, 'get') and not hasattr(self, 'head'):
            self.head = self.get
        # 给我们的对象赋值属性
        self.request = request
        self.args = args
        self.kwargs = kwargs
        '''
        在看python源码的时候,一定要时刻注意面向对象属性方法查找顺序
            先从对象自己找
            再去产生对象的类里面找
            之后再去父类找
        总结:在看源码时,如果遇到 self.x 操作时,一定要时刻注意当前这个self到底是谁
         '''
        return self.dispatch(request, *args, **kwargs)
 http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    # CBV 的 精髓 !!!
    def dispatch(self, request, *args, **kwargs):
        # Try to dispatch to the right method; if a method doesn't exist,
        # defer to the error handler. Also defer to the error handler if the
        # request method isn't on the approved list.
        # 获取当前请求的小写格式 然后比对当前请求方式是否合法
        # 以 get 请求为例
        if request.method.lower() in self.http_method_names:
            '''
            反射:通过字符串来操作对象的属性或方法
            '''
            # self 是我们自己写的类产生的对象
            # handler = getattr(自己写的类产生的对象,'get',当找不到get属性或方法时就会用到第三个参数)
            # 因为我们在自己的类里面重写了 get 方法 ,所以这里的 handler 就变成了我们自己写的 get方法
            # handler = 我们自己写的类里面的get方法
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        # 自动调用 get 方法
        return handler(request, *args, **kwargs)
(3)小结
url(r'^login/', views.MyLogin.as_view()),
- 当我们启动Django项目时
- 会自动触发路由中的方法,调用 as_view 方法并自执行
- 在执行后我们查看 as_view 方法的源码 发现
- 在依次给我们的对象赋值后,最终返回了一个自执行的 dispatch 方法
 
- 于是我们又去查看了 dispatch 方法
- 在 dispatch 内部 ,先是将请求方式转换并进行校验
- 然后开始校验需要调用的方法的调用位置,校验成功并拿到需要执行的方法执行
 
- 在自己写的类中如果有相关的方法,会首先调用我们重写的类方法,并返回执行结果
- 如果自己的类里面没有该方法 会去自己的父类中调用 父类的方法
- 如果父类 以及 基类 都找不到则报错,抛出异常
 
 
- 如果自己的类里面没有该方法 会去自己的父类中调用 父类的方法
本文来自博客园,作者:Chimengmeng,转载请注明原文链接:https://www.cnblogs.com/dream-ze/p/17559606.html

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号