zoukankan      html  css  js  c++  java
  • Django 视图层

    Django视图层

       Django视图层相关的知识都是在APP下的views.py中所应用的,视图层的主要功能就是如何处理由路由层解析过来的request请求,主要包含以下几块内容

      1.如何处理request请求对象

      2.如何返回一个HTML文档或其他内容给前端页面

      3.如何对接上数据库实时获取数据

    返回内容

    HttpResponse

       HttpResoponse()接收一个字符串并返回。

    from django.shortcuts import HttpResponse
    
    def return_HttpResponse(request):
        return HttpResponse("返回的内容")
    

    redirect

       redirect()接受一个url并返回,状态码为302,即重定向。

    from django.shortcuts import redirect
    
    def return_redirect(request):
        return redirect("http://www.google.com") # 跳转到www.Google.com
    

    render

       render()有多个参数,其中第一个参数为返回request请求对象,其他参数则用于返回HTML文档及其局部命名空间内变量用作模板渲染。

    参数返回项
    参数1 request对象
    参数2 HTML文档
    参数3 局部命名空间变量(字典形式从换入),或locals()函数
    from django.shortcuts import render
    
    def return_render(request):
    
        user_msg = {"name":"Yunya","age":18,"gender":"male"}
    
        return render(request,"login.html",{"user_msg":user_msg}) 
    
        # 参数3:{"user_msg":user_msg}  只传递user_msg到HTML文档
        # 参数3:locals() 将当前函数的命名空间(return_render)下的所有变量传递到HTML文档
    

    Jsonresponse

       JsonresponseDjango中自带的一个基于json模块的封装,可以直接返回json类型的数据至模板层的前端页面。

      1.只支持字典,如果想传递其他数据类型需要将safe设置为False

      2.如果想让转换后的json格式字符串中存在中文,请设置参数json_dumps_params={"ensure_ascii":False},即关闭自动转码

    from django.http import JsonResponse
    
    def f1(request):
        data = {"name":"云崖","age":18}
        return JsonResponse(data,json_dumps_params={"ensure_ascii":False})  # 只支持字典,关闭自动转码
    

    扩展知识

       其实不管是redirect也好,reder也罢,或者说Jsonresponse也好,实际上内部都是返回了HttpResponse,比如render的模板替换其实原理非常简单,就是将后端的变量映射到模板上,就好比于字符串替换方法,把字符串替换成一个对象。

       展示的模板页面其实就是经过替换之后来完成的。

    def f1(request):
        data = {"name":"云崖","age":18}
    
        from django.template import Template,Context
        
        res = Template("<h1>{{user}}<h1>")  # 模板,将HTML代码渲染成模板
        con = Context({"user":{"name":"Yunya"}}) # 替换的内容封装成Context对象
        ret = res.render(con) # 执行替换
        
        return HttpResponse(ret)
    
    

    request

       request对象即是用户发送过来的资源请求,内置了很多方法及对象供我们使用。

       其实Djangorequest对象是基于wsgirefenv的一个封装,是我们获取资源请求中的信息更加方便。

    提交方式

       通过对request.method指向判断即可判定提交方式。

    from django.shortcuts import render
    from django.shortcuts import HttpResponse
    
    def register(request):
    
        if request.method == "GET":
            print(request.GET)
            return render(request,"register.html")
    
        elif request.method == "POST":
            print(request.POST)
            username = request.POST.get("username")
            password = request.POST.get("password")
            hobby = request.POST.getlist("hobby")
            
            print(
                """
                username:{0},
                password:{1},
                hobby:{2}
                """
                .format(username,password,hobby)
            )
            return HttpResponse("注册成功")
    

    GET&POST

       request.GETrequest.POST都是两个QueryDict对象,其中保存了用户提交过来的数据(不包含文件)。

       如下示例,在地址栏中手动填入数据,request.GET获取信息如下:

    http://127.0.0.1:8000/register/?msg1=Django&msg2=request # 手动在地址栏填入的数据
    
    print(request.GET)
    <QueryDict: {'msg1': ['Django'], 'msg2': ['request']}>
    

    数据获取

       数据获取可使用get()getlist()方法。

       get()用于从QueryDict中获取单个值,但无法获取多个,如<QueryDict: {'hobby': ['music','game']}>,那么获取到的始终是列表中的最后一个。

       而getlist()可获取多个,以列表形式进行返回,比如用户注册中有爱好等多选行为,应该使用getlist()进行获取。

       如下示例结合提交方式与数据获取,将一个视图函数分成两部分,结合不同的请求方式返回不同的请求内容。

    from django.shortcuts import render
    from django.shortcuts import HttpResponse
    
    def register(request):
    
        if request.method == "GET":
            print(request.GET)
            return render(request,"register.html")
    
        elif request.method == "POST":
            print(request.POST)
            username = request.POST.get("username")
            password = request.POST.get("password")
            hobby = request.POST.getlist("hobby")  # 获取用户爱好等多选项时,应该使用getlist
            
            print(
                """
                username:{0},
                password:{1},
                hobby:{2}
                """
                .format(username,password,hobby)
            )
            return HttpResponse("注册成功")
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src='https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js'></script>
        <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css' integrity='sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u' crossorigin='anonymous'>
        <script src='https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js' integrity='sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa' crossorigin='anonymous'></script>
    
        <title>Document</title>
    </head>
    <body>
        
        <h1 class="text-center">欢迎注册</h1>
            
            <div class="container">
                
                <div class="col-xs-8 col-sm-8 col-md-8 col-lg-8 col-xs-offset-2 col-sm-offset-2 col-md-offset-2 col-lg-offset-2">
                    <form action="" method="POST">  <!-- 当action不填时,提交至当前页面 -->
                        <p><input type="text" name="username" class="form-control"></p>
                        <p><input type="password" name="password" class="form-control"></p>
                        <p class="checkbox-inline"><input type="checkbox" name="hobby" value="basketball">篮球</p>
                        <p class="checkbox-inline"><input type="checkbox" name="hobby" value="football">足球</p>
                        <p></p>
                        <p><input type="submit" class="btn btn-success form-control"></p>
    
                    </form>
                </div>
                
            </div>
            
    
    </body>
    </html>
    

    文件获取

       对于form中上传的文件,需要用request.FILES.get()进行获取。进行写入的时候推荐使用chunkS()(其实直接写也没啥问题)。

      注意:前端必须为POST请求类型,并且必须指定enctype属性为multpart/form-data。关于enctypeAjax中已有介绍。

    def f1(request):
    
        if request.method == "GET":
            return render(request,"f1.html")
            
        elif request.method == "POST":
            file_obj = request.FILES.get("file_1")
            print(file_obj.name)
            print(file_obj.size)
    
            import os
            if not os.path.exists("./user_files"):
                os.mkdir("./user_files")
    
            with open("./user_files/{0}".format(file_obj.name),"wb") as f:
                for line in file_obj.chunks():  # 推荐使用chunks()
                    f.write(line)
    
            return HttpResponse("写入完成")
    
        <form action="{% url 'app01:f1' %}" method="POST" enctype="multipart/form-data">
            <p><input type="file" name="file_1"></p>
            <p><button type="submit">上传文件</button></p>
        </form>
    

    路径获取

       获取请求资源路径的方式有三种,两种属性一种方法,记住两种即可。

    属性/方法描述
    request.path 只拿请求资源路径,不拿GET请求的数据
    request.path_info 同上
    request.get_full_path() 即拿请求资源路径,也拿GET请求中的数据
    http://127.0.0.1:8000/app01/f1/?id=1&username=Yunya
    
    print(request.path)  # /app01/f1/
    print(request.path_info) # /app01/f1/
    print(request.get_full_path()) # /app01/f1/?id=1&username=Yunya
    

    原生数据

       通过request.body属性可获取原生的资源路径请求,包括请求头,请求体等。

      注意:拿到的是字节类型,这个是在页面请求方式中没有注明任何数据编码格式,将会放入request.body

    http://127.0.0.1:8000/app01/f1/  # POST方式提交,表单未input有个file类型,其他都没有了。
    
    print(request.body)
    
    b'------WebKitFormBoundary7YEgYjof25C6E8z9
    Content-Disposition: form-data; name="file_1"; filename=""
    Content-Type: application/octet-stream
    
    
    ------WebKitFormBoundary7YEgYjof25C6E8z9--
    '
    

    请求头

       通过request.META可获取到请求头,这个请求头非常有用。

       它中间封装了很多属性,比如有一个属性就是来判断是将数据存放进request.body/request.POST/request.GET/request.FEILS种的哪一个里面。

       我们可以看一下request.POST的源码:

       def _get_post(self):
            if not hasattr(self, '_post'):
                self._load_post_and_files()  # 执行
            return self._post
    
    # self._load_post_and_files() 方法中的两个重要判断
    
    if self.content_type == 'multipart/form-data':  # 判断请求头中请求方式的编码格式,这个会将POST提交中的文件放入reques.FILES里
                   
    elif self.content_type == 'application/x-www-form-urlencoded':  # 判断是否有该请求头,有的话放入request.POST中
    

    FBV&CBV

    区别差异

       FBV即在视图层里通过函数的方式进行逻辑处理。

       CBV则是在视图层里通过类的方式进行逻辑处理。

       CBV对比FBV有一个很明显的又是,就是能够自动的识别资源请求的提交方式。

       其实CBV内部就是FBV

    基本使用

       使用CBV需要注意导入模块View,并且书写的类要继承该类。

       同时在urls.py做路由解析时要使用as_view()来进行解析。

    from django.conf.urls import url
    from app01 import views
    
    urlpatterns = [
        url('^c1/', views.C1.as_view())  # 注意,需要加括号
    ]
    
    from django.shortcuts import HttpResponse
    from django.views import View # 导入
    
    class C1(View): # 必须继承
     
        def get(self,request):  # 当get请求来,执行该方法
            return HttpResponse("get方法...")
    
        def post(self,request): # 当post请求来,执行该方法
            return HttpResponse("post方法...")
    

    源码分析

       CBV如何做到区分请求方式的?这个要从路由层的as_view()方法开始看。

     @classonlymethod  # 类方法
        def as_view(cls, **initkwargs):
            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) # self == C1,即我们自己定义的类
                if hasattr(self, 'get') and not hasattr(self, 'head'): # 如果有C1类有get方法并且没有head方法
                    self.head = self.get 
                self.request = request  # 参数赋值
                self.args = args  # 参数赋值
                self.kwargs = kwargs  # 参数赋值
                return self.dispatch(request, *args, **kwargs)  # 执行并返回
            view.view_class = cls  # cls 即 C1 类
            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=()) # 可以看到,这里的cls.dispatch是一个属性或是没运行的方法,根据属性查找顺序。往C1的继承类中找,会找到dispatch()方法,并且注意,即使是方法这里并没有执行,所以会接着往下走
            return view   # 返回view,接下来才执行。
    

       经过闭包函数的返回,下面将会进行变形。

    from django.conf.urls import url
    from app01 import views
    
    urlpatterns = [
        # url('^c1/', views.C1.as_view())  # 注意,需要加括号
        url('^c1/', views.C1.view) 自动执行view方法,其实就是执行闭包函数中的逻辑块
    ]
    

       继续向下取找View类中的dispatch属性/方法。

        def dispatch(self, request, *args, **kwargs):
    		# 说白了就是判断请求方式,是不是在这个 self.http_method_names 里头,如果在handler就是一个请求模式的方法,比如get或者post
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            return handler(request, *args, **kwargs) # 执行并返回了
    

       好了,也就是说最后的as_view()会执行View.dispatch()这个方法,这个方法内部其实就会执行GET或者POST方法。

      django-CBV源代码分析

    方法扩展

       基于上面的源码分析,反正都会运行dispatch(),那么我们就可以对其进行方法扩展。

    from django.shortcuts import HttpResponse
    from django.views import View
    
    class C1(View):
        def dispatch(self, request, *args, **kwargs):
            # 添加逻辑,如登录。必须登录后才能进行操作
            return super(C1,self).dispatch(request, *args, **kwargs)
        def get(self,request):
            return HttpResponse("get方法...")
    
        def post(self,request):
            return HttpResponse("post方法...")
    

    FBV装饰器

       给FBV添加装饰器,一般我们会单独将request提取出来。

    from django.shortcuts import HttpResponse
    
    def wrapp(func):
    	def inner(request,*args,**kwargs):
    		return func(request,*args,**kwargs):
    	return inner
    
    @wrapp
    def other(request):
    	return HttpResponse("other...")
    
    

    CBV装饰器

       CBV添加装饰器的方式有三种。针对不同的应用场景可以使用不同的装饰器添加方法,但是有一点是相同的。

       都需要先导入下面这个模块:

    from django.utils.decorators import method_decorator
    

       方式一:为单独的某个功能添加装饰器。

    from django.shortcuts import HttpResponse
    from django.views import View
    from django.utils.decorators import method_decorator
    
    def wrapp(func):
    	def inner(request,*args,**kwargs):
    		return func(request,*args,**kwargs):
    	return inner
    
    class Other(View):
        @method_decorator(wrapp)
        def get(self, request):
            return HttpResponse("get方法...")
            
         @method_decorator(wrapp)
        def post(self,request):
            return HttpResponse("post方法...")
    

       方式二:为CBV类添加装饰器,可指定该装饰器作用与类下的那些方法(可添加多个)。

    from django.shortcuts import HttpResponse
    from django.views import View
    from django.utils.decorators import method_decorator
    
    def wrapp(func):
    	def inner(request,*args,**kwargs):
    		return func(request,*args,**kwargs):
    	return inner
    
    
    @method_decorator(wrapp,name="get")
    @method_decorator(wrapp,name="post")
    class Other(View):
        def get(self,request):
            return HttpResponse("get方法...")
    
        def post(self,request):
            return HttpResponse("post方法...")
    

       方式三:为dispatch方法添加装饰器,该装饰器会作用于所有的方法。(因为入口方法不是post,就是get

    from django.shortcuts import HttpResponse
    from django.views import View
    from django.utils.decorators import method_decorator
    
    def wrapp(func):
    	def inner(request,*args,**kwargs):
    		return func(request,*args,**kwargs):
    	return inner
    
    
    class Other(View):
    	@method_decorator(wrapp)
        def dispatch(self, request, *args, **kwargs):
            return super(Other,self).dispatch(request, *args, **kwargs)
            
        def get(self,request):
            return HttpResponse("get方法...")
    
        def post(self,request):
            return HttpResponse("post方法...")
    
  • 相关阅读:
    基于java对doc文档的分词,导入数据库
    软件架构 软件质量属性(淘宝)
    c/c++ Java python
    阅读架构漫谈心得体会
    关于ubuntu16.4在vmware中打开只显示桌面,桌面闪烁的问题
    暑假总结07
    暑假总结06
    RandomStringUtils
    spring-data-elasticsearch
    Socket和ObjectOutputStream问题
  • 原文地址:https://www.cnblogs.com/Yunya-Cnblogs/p/13687843.html
Copyright © 2011-2022 走看看